繁体   English   中英

如何在前端和后端之间持久化 Firebase Auth state? (Firebase 身份验证 + React + Express)

[英]How to persist the Firebase Auth state between front-end and backend ? (Firebase Auth + React + Express)

我正在尝试在后端使用 Firebase Auth,但我似乎也无法在前端拥有相同的Auth instance

后端:

'use strict';

import { firebaseAdmin, auth } from '../firebase.js';
import deleteCollection from '../helpers/deleteCollection.js';
import User from '../models/user.js';
import {
  createUserWithEmailAndPassword,
  updateProfile,
  signInWithEmailAndPassword,
  signOut,
  setPersistence,
  browserLocalPersistence,
} from 'firebase/auth';

const firestore = firebaseAdmin.firestore();

const register = async (req, res, next) => {
  try {
    // name, email, password
    const { name, email, password, avatar } = req.body;
    console.log('sent from frontend', { name, email, password });
    // Check if email or password were sent
    if (!email || !password) {
      return res.status(422).json({
        email: 'Email is required !',
        password: 'Password is required !',
      });
    }
    const usersCollection = firestore.collection('users');
    // Reference to a QuerySnapshot whith all users that have the requested name
    const userSnapshot = await usersCollection.where('name', '==', name).get();
    // Check if user already exists:
    if (!userSnapshot.empty) {
      throw new Error('Username is taken !');
    } else {
      await setPersistence(auth, browserLocalPersistence);
      // Firebase Auth Create User
      await createUserWithEmailAndPassword(auth, email, password);
      // User is signed in
      const user = auth.currentUser;
      if (user) {
        await updateProfile(user, {
          displayName: name,
        });
        const setUser = {
          id: user.uid,
          name: user.displayName,
          avatar: avatar,
        };
        await usersCollection.doc(setUser.id).set(setUser);
        res.status(201).send(setUser);
      } else {
        throw new Error('No user');
      }
    }
  } catch (error) {
    const errorCode = error.code;
    const errorMessage = error.message;

    res.status(400).send(errorMessage);
    console.log(errorCode, errorMessage);
  }
};

const login = async (req, res, next) => {
  try {
    const { email, password } = req.body;

    await setPersistence(auth, browserLocalPersistence);
    const userCred = await signInWithEmailAndPassword(auth, email, password);

    const usersCollection = firestore.collection('users');
    const userSnapshot = await usersCollection
      .where('name', '==', userCred.user.displayName)
      .get();
    if (userSnapshot.empty) {
      throw new Error('User does not exist !');
    } else {
      let user;

      userSnapshot.forEach((doc) => (user = { ...doc.data() }));
      res.status(200).send(user);
    }
  } catch (error) {
    res.status(404).send(error.message);
    console.log(error);
  }
};

const logout = async (req, res, next) => {
  try {
    // const { name, email, password, avatar } = req.body;

    await signOut(auth);
    res.sendStatus(200);
  } catch (error) {
    const errorCode = error.code;
    const errorMessage = error.message;

    res.status(404).send(errorMessage);
    console.log(error);
  }
};

我使用 Redux thunkAPI 调用注册、登录和注销:

const register = async (userData) => {
  const response = await axios.post(API_REGISTER, userData, {
    headers: {
      // Overwrite Axios's automatically set Content-Type
      'Content-Type': 'application/json',
    },
  });

  if (response.data) {
    // localStorage.setItem('user', JSON.stringify(response.data));
  }
  return response.data;
};

const login = async (userData) => {
  const response = await axios.post(API_LOGIN, userData, {
    headers: {
      // Overwrite Axios's automatically set Content-Type
      'Content-Type': 'application/json',
    },
  });

  if (response.data) {
    // localStorage.setItem('user', JSON.stringify(response.data));
  }
  return response.data;
};

const logout = async () => {
  const response = await axios.get(`${API_LOGOUT}`);

  if (response.data) {
    localStorage.removeItem('user');
  }
  return response.data;
};
export const register = createAsyncThunk(
  'user/register',
  async (user, thunkAPI) => {
    try {
      return await userService.register(user);
    } catch (error) {
      return thunkAPI.rejectWithValue(error.response.data);
    }
  }
);

export const login = createAsyncThunk('user/login', async (user, thunkAPI) => {
  try {
    return await userService.login(user);
  } catch (error) {
    return thunkAPI.rejectWithValue(error.response.data);
  }
});

export const logout = createAsyncThunk('user/logout', async (_, thunkAPI) => {
  try {
    return await userService.logout();
  } catch (error) {
    return thunkAPI.rejectWithValue(error.response.data);
  }
});

我可以注册用户,登录和注销,但如果我点击刷新,我会被注销。

我无法在前端和后端之间保留 Firebase Auth state。

这是私有路由组件

import { useSelector } from 'react-redux';
import { Navigate, useLocation } from 'react-router-dom';

import { auth } from '../../firebase';
import { useAuthState } from 'react-firebase-hooks/auth';
import { useEffect } from 'react';
import { useState } from 'react';

let isAuth;

export default function PrivateRoute({ children }) {
  const location = useLocation();
  const [user, setUser] = useState();
  // const isAuth = useSelector((state) => state.user.user);

  // const [user, loading, error] = useAuthState(auth);

  // useEffect(() => {
  //   if (loading) return;
  //   if (user) {
  //     isAuth = true;
  //     console.log(user);
  //   }
  // }, [user, loading]);

  useEffect(() => {
    auth.onAuthStateChanged(setUser);
  }, []);

  return user ? (
    children
  ) : (
    <Navigate
      replace={true}
      to='/login'
      state={{ from: `${location.pathname}${location.search}` }}
    />
  );
}

正如您从注释代码中看到的那样,我在此处发布之前尝试了多种方法,但没有任何效果。

我不想将身份验证逻辑从后端移动到前端。

我只想在后端到前端之间访问相同的 Auth state。

Firebase 不支持您使用的方法。 您应该只在前端验证用户,而不是在后端验证用户。 前端 SDK 将保留一个标识用户的令牌。 然后,您在每次调用时将该令牌传递给后端,并使用它来验证用户,以便后端可以决定是否允许他们尝试执行的操作。 该方案在文档中进行了描述,我强烈建议您查看:

如果您的 Firebase 客户端应用程序与自定义后端服务器通信,您可能需要识别该服务器上当前登录的用户。 要安全地执行此操作,请在成功登录后,使用 HTTPS 将用户的 ID 令牌发送到您的服务器。 然后,在服务器上,验证 ID 令牌的完整性和真实性并从中检索 uid。 您可以使用以这种方式传输的 uid 来安全地识别您服务器上当前登录的用户。

同样,不要尝试使用前端 SDK 在后端登录用户 - 这不受支持且无法扩展。 仅在后端使用 Firebase Admin SDK 来验证从前端传递的用户 ID 令牌。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM