如何在前端和后端之间保持Firebase Auth状态?(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状态。

这是专用路由组件

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状态。

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

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

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

最新更新