Аутентификация на базе Firebase: как разрешить ошибку ‘TypeError: не удается прочитать свойство ‘getIdToken’ из null’

#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»?

4. Документация здесь

Ответ №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 на своей странице входа.