开发者问题收集

Firebase 身份验证:如何解决“TypeError:无法读取 null 的属性‘getIdToken’”错误

2020-10-02
2513

我是实施身份验证的新手,对如何解决我遇到的问题有些困惑。

我正在构建一个 React 应用,并希望登录的用户能够通过 GigRegister 组件将数据提交到 Firebase 数据库。但是,当我尝试提交信息时,出现以下错误:


    TypeError: Cannot read property 'getIdToken' of null

关于如何解决这个问题,您有什么想法吗?

这是登录页面的代码(登录后重定向到 Gig 注册页面):

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;

..这是 Gig 注册页面:


    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})
    })
})

3个回答

提示

TypeError:无法读取 null 的属性“getIdToken”

问题

您所写的内容:

firebase.auth().currentUser.getIdToken()

编译器接收到的内容:

null.getIdToken()

null 没有 getIdToken 方法,因此当您尝试调用该函数时,您会收到错误 TypeError:无法读取 null 的属性“getIdToken”

解决方案

您需要在代码中的某个地方调用 firebase.auth().signInWithEmailAndPassword(email, password) ,然后 currentUser 的详细信息将保存在浏览器中 firebaseLocalStorageDB

然后,您可以正确调用 firebase.auth().currentUser.getIdToken()

此外,我不明白您为什么在这里使用 axios 。您应该使用 firebase 读/写 API,它 可在此处获得 。从上到下阅读文档,您不会后悔的!

Alex Mckay
2020-10-02

您出于什么原因从后端调用 signInWithEmailAndPassword ?据我所知, app.post('/login', ...) 仅验证正文,调用 signInWithEmailAndPassword 并发送令牌作为响应。

我建议将此逻辑移至客户端。在这种情况下,firebase auth 对象将具有 currentUser

如果您不想这样做,则必须在客户端的 POST 登录请求后调用 auth.signInWithCustomToken 。文档: 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. 将 auth 导入到 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. 替换 GigRegister 组件中的身份验证导入

import { auth } from 'firebase/app'

6. 替换 GigRegister 组件中的身份验证调用

/* handleSubmit method */
auth().currentUser.getIdToken().then(console.log)
Kirill Skomarovskiy
2020-10-04

firebase.auth().currentUsernull 。您必须在登录页面登录 Firebase。

Aleks
2020-10-02