我正在开发一个使用 Node.js/Express.js 作为后端的 Web 应用程序,我使用 Firebase 进行用户身份验证,并使用 Firebase Admin SDK 管理用户注册等。
当用户想要登录时,我使用 Firebase Client SDK 登录他,如下所示:
// Handling User SignIn
$('#signin').on('click', function(e){
e.preventDefault();
let form = $('#signin-form'),
email = form.find('#email').val(),
pass = form.find('#password').val(),
errorWrapper = form.find('.error-wrapper');
if(email && pass){
firebase.auth().signInWithEmailAndPassword(email, pass)
.catch(err => {
showError(errorWrapper, err.code)
});
}else {
showError(errorWrapper, 'auth/required');
}
});
在这段代码下面,我设置了一个观察者来监视用户何时成功登录,成功登录后,我获得了一个 Firebase ID 令牌,我将其发送到服务器上的端点,以将其交换为具有相同声明的会话 cookie ID 令牌,因为后者会在 1 小时后过期。
// POST to session login endpoint.
let postIdTokenToSessionLogin = function(url, idToken, csrfToken) {
return $.ajax({
type: 'POST',
url: url,
data: {
idToken: idToken,
csrfToken: csrfToken
},
contentType: 'application/x-www-form-urlencoded'
});
};
// Handling SignedIn Users
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
user.getIdToken().then(function(idToken) {
let csrfToken = getCookie('csrfToken');
return postIdTokenToSessionLogin('/auth/signin', idToken, csrfToken)
.then(() => {
location.href = '/dashboard';
}).catch(err => {
location.href = '/signin';
});
});
});
} else {
// No user is signed in.
}
});
服务器上的登录端点如下所示:
// Session signin endpoint.
router.post('/auth/signin', (req, res) => {
// Omitted Code...
firebase.auth().verifyIdToken(idToken).then(decodedClaims => {
return firebase.auth().createSessionCookie(idToken, {
expiresIn
});
}).then(sessionCookie => {
// Omitted Code...
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({
status: 'success'
}));
}).catch(err => {
res.status(401).send('UNAUTHORIZED REQUEST!');
});
});
我创建了一个中间件来验证用户会话 cookie,然后再让他访问受保护的内容,如下所示:
function isAuthenticated(auth) {
return (req, res, next) => {
let sessionCookie = req.cookies.session || '';
firebase.auth().verifySessionCookie(sessionCookie, true).then(decodedClaims => {
if (auth) {
return res.redirect('/dashboard')
} else {
res.locals.user = decodedClaims;
next();
}
}).catch(err => {
if (auth) next();
else return res.redirect('/signin')
});
}
}
为了在视图上显示用户信息,我将解码的声明设置为res.locals.user
变量并将其传递到下一个中间件,我在其中渲染视图并像这样传递该变量。
router.get('/', (req, res) => {
res.render('dashboard/settings', {
user: res.locals.user
});
});
到目前为止,一切都很好,现在问题出现在用户转到仪表板更改他的信息(姓名和电子邮件)之后,当他将包含他的姓名和电子邮件的表单提交到服务器上的端点时,我使用 Firebase 更新他的凭据管理软件开发工具包
// Handling User Profile Update
function settingsRouter(req, res) {
// Validate User Information ...
// Update User Info
let displayName = req.body.fullName,
email = req.body.email
let userRecord = {
email,
displayName
}
return updateUser(res.locals.user.sub, userRecord).then(userRecord => {
res.locals.user = userRecord;
return res.render('dashboard/settings', {
user: res.locals.user
});
}).catch(err => {
return res.status(422).render('dashboard/settings', {
user: res.locals.user
});
});
}
现在,当用户提交表单时,视图会更新,因为我设置了res.locals.user
变量为新的userRecord
但是一旦他刷新页面,视图就会显示旧的凭据,因为在对受保护内容进行任何获取请求之前,中间件isAuthenticated
被执行,后者从会话 cookie 中获取用户信息,其中包含更新之前的旧用户凭据。
到目前为止,这些是我得出的结论以及我尝试做的事情:
如果我希望视图正确呈现,我应该退出并再次登录以获取新的 Firebase ID 令牌来创建新的会话 cookie,这不是一个选项。
我尝试通过从 Admin SDK 创建新的 ID 令牌来刷新会话 cookie,但似乎没有可用的此选项,并且我无法通过客户端 SDK 执行此操作,因为用户已经登录。
存储 ID 令牌以便稍后在创建会话 cookie 时使用并不是一种选择,因为它们会在 1 小时后过期。
在在这里发帖之前,我用谷歌搜索了这个问题,所以非常感谢任何帮助。