#reactjs #firebase #firebase-authentication
#reactjs #firebase #firebase-аутентификация
Вопрос:
Я новичок в реализации аутентификации и испытываю некоторую путаницу в том, как решить возникшую у меня проблему.
Я создаю приложение react и хочу, чтобы зарегистрированные пользователи могли отправлять данные через GigRegister
компонент в базу данных firebase. Однако, когда я пытаюсь отправить информацию, я получаю следующую ошибку:
TypeError: Cannot read property 'getIdToken' of null
Есть мысли о том, как я могу это решить?
Вот код для страницы входа в систему (которая перенаправляет на страницу регистрации концерта при входе в систему):
import React from 'react'
import Header from './Header'
import Button from '@material-ui/core/Button'
import axios from 'axios'
import {Link} from 'react-router-dom'
class Login extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: "",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit(e) {
console.log("submit reached");
e.preventDefault();
const loginData = {
email: this.state.email,
password: this.state.password,
};
axios("http://localhost:5000/gig-fort/us-central1/api/login", {
method: "POST",
headers: {
"content-type": "application/json",
},
data: loginData,
})
.then((res) => {
console.log(res.data);
this.props.history.push("/gigregister");
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<>
<div>
<Header />
</div>
<Link to="/Homepage" style={{ textDecoration: "none" }}>
<h1 className="login-header">Gigs this week</h1>
</Link>
<div className="login-main">
<div className="login">
<h2>Venue login</h2>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="email"
placeholder="email"
onChange={this.handleChange}
/>
<br></br>
<input
type="password"
name="password"
placeholder="password"
onChange={this.handleChange}
/>
<div className="button">
<Button type="submit">Submit</Button>
</div>
</form>
</div>
<Link to="/venueregister" style={{ textDecoration: "none" }}>
<h2 style={{ color: "#b49650" }}>Register a venue</h2>
</Link>
</div>
</>
);
}
}
export default Login;
.. и вот страница регистрации концерта:
import React from "react";
import Header from "./Header";
import TextField from "@material-ui/core/TextField";
import Button from "@material-ui/core/Button";
import axios from "axios";
import * as firebase from 'firebase'
firebase.initializeApp({
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "gig-fort",
storageBucket: "gig-fort.appspot.com",
messagingSenderId: "",
appId: ""
})
class GigRegister extends React.Component {
constructor() {
super();
this.state = {
name: "",
venue: "",
time: "",
date: "",
genre: "",
tickets: "",
price: "",
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit(e) {
console.log("submit function reached");
e.preventDefault();
const gigData = {
name: this.state.name,
venue: this.state.venue,
time: this.state.time,
date: this.state.date,
genre: this.state.genre,
tickets: this.state.tickets,
price: this.state.price,
};
firebase.auth().currentUser.getIdToken().then(function(token) {
axios("http://localhost:5000/gig-fort/us-central1/api/createGigListing", {
method: "POST",
headers: {
"content-type": "application/json",
"Authorization": "Bearer " token,
},
data: gigData,
})
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<div className="gig-register">
<Header />
<h1 className="header-gigReg">Register a gig</h1>
<form onSubmit={this.handleSubmit}>
<TextField
placeholder="Event name"
defaultValue="Event name"
id="name"
name="name"
onChange={this.handleChange}
/>
<TextField
placeholder="Venue"
defaultValue="Venue"
id="venue"
name="venue"
onChange={this.handleChange}
/>
<TextField
placeholder="Time"
defaultValue="Time"
type="time"
label="Enter start time"
id="time"
name="time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
onChange={this.handleChange}
/>
<TextField
id="date"
label="Select date"
type="date"
defaultValue="2017-05-24"
InputLabelProps={{
shrink: true,
}}
onChange={(e) => {
this.setState({ date: e.target.value });
}}
/>
<TextField
placeholder="Genre"
defaultValue="Genre"
id="genre"
name="genre"
onChange={this.handleChange}
/>
<TextField
placeholder="Tickets"
defaultValue="Tickets"
id="tickets"
name="tickets"
onChange={this.handleChange}
/>
<TextField
placeholder="Price"
defaultValue="Price"
id="price"
name="price"
onChange={this.handleChange}
/>
<Button type="submit">Submit</Button>
</form>
</div>
);
}
}
export default GigRegister
Внутренние функции:
app.post('/login', (req,res)=> {
const user = {
email: req.body.email,
password: req.body.password
}
let errors = {}
if(isEmpty(user.email)){
errors.email = 'Must not be empty'
}
if(isEmpty(user.password)){
errors.password = 'Must not be empty'
}
if(Object.keys(errors).length >0){
return res.status(400).json(errors)
}
firebase.auth().signInWithEmailAndPassword(user.email, user.password)
.then((data) => {
return data.user.getIdToken()
})
.then(token => {
return res.json({token})
})
.catch(err => {
if(err.code === 'auth/wrong-password'){
return res.status(403).json({general: 'Wrong credentials, please try again'})
}
return res.status(500).json({error: err.code})
})
})
Ответ №1:
Подсказка
TypeError: Cannot read property 'getIdToken' of null
Проблема
Что вы написали:
firebase.auth().currentUser.getIdToken()
Что получает компилятор:
null.getIdToken()
null
не имеет getIdToken
метода, поэтому при попытке вызвать функцию вы получаете сообщение об ошибке TypeError: Cannot read property 'getIdToken' of null
.
Решение
Где-то в вашем коде вам нужно вызвать firebase.auth().signInWithEmailAndPassword(email, password)
, после currentUser
чего данные сохраняются в браузере firebaseLocalStorageDB
.
Затем вы можете firebase.auth().currentUser.getIdToken()
правильно вызвать.
Кроме того, я не понимаю, почему вы используете axios
здесь. Вы должны использовать API чтения / записи firebase, который доступен здесь . Прочитайте документы сверху донизу, вы не пожалеете об этом!
Комментарии:
1. Спасибо за предложение — однако у меня есть
firebase.auth().signInWithEmailAndPassword(email, password)
в отдельном репозитории — отредактировал свой первоначальный вопрос, чтобы включить его.2. Проверьте, хранится ли оно в вашем локальном хранилище. В Chrome:
open dev tools -> click Application tab -> locate firebaseLocalStorageDB
. Если там есть ключ, дайте мне знать, если нет, ваш пользователь сохраняется неправильно.3. Я проверил, и токен хранится в локальном хранилище, но, похоже, я не могу найти ничего вызываемого
firebaseLocalStorageDB
. У меня такое чувство, что мне нужно указать пользователя в методе » .onAuthStateChanged»?
Ответ №2:
По какой причине вы вызываете signInWithEmailAndPassword
серверную часть? Как я вижу app.post('/login', ...)
, просто проверяет тело, вызывает signInWithEmailAndPassword
и отправляет токен в качестве ответа.
Я предлагаю перенести эту логику на клиента. В этом случае auth
объект firebase будет иметь currentUser
.
Если вы не хотите, вы должны вызывать auth.signInWithCustomToken
после запроса POST login на стороне клиента. Документ: https://firebase.google.com/docs/auth/web/custom-auth#authenticate-with-firebase .
Обновить:
1. Создайте новый файл firebase.js
import * as firebase from 'firebase/app'
import 'firebase/auth'
/* if you use any other firebase modules import they here.
* for example:
* import "firebase/database"
*/
export default function initFirebase() {
firebase.initializeApp({
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: 'gig-fort',
storageBucket: 'gig-fort.appspot.com',
messagingSenderId: '',
appId: ''
})
}
2. инициализируйте приложение из index.js (основной файл вашего проекта)
import initFirebase from 'path/to/firebase.js';
// ...imports
initFirebase();
3. добавьте авторизацию импорта в Login
компонент;
import React from 'react'
import Header from './Header'
import Button from '@material-ui/core/Button'
import axios from 'axios'
import { Link } from 'react-router-dom'
import { auth } from 'firebase/app'
4. замените свой метод отправки в Login
компоненте
handleSubmit(e) {
e.preventDefault()
const { email, password } = this.state
const { history } = this.props
auth().signInWithEmailAndPassword(email, password)
.then((res) => {
console.log(res)
history.push('/gigregister')
})
.catch(console.error)
}
5. замените импорт auth в GigRegister
компоненте
import { auth } from 'firebase/app'
6. замените вызов аутентификации в GigRegister
компоненте
/* handleSubmit method */
auth().currentUser.getIdToken().then(console.log)
Комментарии:
1. Я попытался переместить интерфейс
sigInWithEmailAndPassword
i, но получил ту же ошибку2. @JoshSimon Да 👍
Ответ №3:
firebase.auth().currentUser
есть null
.Вы должны войти в Firebase на своей странице входа.