[英]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.