Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/457.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何刷新Firebase会话Cookie_Javascript_Node.js_Firebase_Express_Firebase Authentication - Fatal编程技术网

Javascript 如何刷新Firebase会话Cookie

Javascript 如何刷新Firebase会话Cookie,javascript,node.js,firebase,express,firebase-authentication,Javascript,Node.js,Firebase,Express,Firebase Authentication,我正在开发一个web应用程序,后端使用Node.js/Express.js,使用Firebase进行用户身份验证,并使用Firebase Admin SDK管理用户注册等 当用户想要登录时,我使用Firebase客户端SDK将其登录,如下所示: // Handling User SignIn $('#signin').on('click', function(e){ e.preventDefault(); let form = $('#signin-form'),

我正在开发一个web应用程序,后端使用Node.js/Express.js,使用Firebase进行用户身份验证,并使用Firebase Admin SDK管理用户注册等

当用户想要登录时,我使用Firebase客户端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');
    }
});
// 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!');
    });
});
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')
        });
    }
}
在这段代码下面,我设置了一个观察者来观察用户何时成功登录,在成功登录后,我获得了一个Firebase ID令牌,我将其发送到服务器上的一个端点,以将其交换为会话cookie,该会话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.
    }
});
服务器上的登录终结点如下所示:

// 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');
    }
});
// 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!');
    });
});
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')
        });
    }
}
我创建了一个中间件,用于在允许用户访问受保护的内容之前验证用户会话cookie,如下所示:

// 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');
    }
});
// 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!');
    });
});
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 Admin SDK更新其凭据

// 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
变量设置为new
userRecord
,但一旦他刷新页面,视图就会显示旧凭据,因为在对受保护内容的任何get请求之前,中间件
已验证
会被执行,而后者会被删除会话cookie中的用户信息,其中包含更新前的旧用户凭据

到目前为止,这些是我得出的结论和我试图做的事情:

  • 如果我希望视图正确呈现,我应该注销并再次登录,以获取新的Firebase ID令牌来创建新的会话cookie,而这不是一个选项

  • 我试图通过从Admin SDK创建一个新的ID令牌来刷新会话cookie,但它似乎没有可用的此选项,并且我无法通过客户端SDK进行刷新,因为用户已经登录

  • 存储ID令牌以供以后创建会话cookie时使用不是一个选项,因为它们在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.
        }
    });
    

我在这里发布之前通过谷歌搜索解决了这个问题,因此非常感谢您的帮助。

我的一个应用程序也面临着类似的情况。我认为答案就在于这些线索

Firebase Auth为依赖会话cookie的传统网站提供服务器端会话cookie管理。与客户端短期ID令牌相比,此解决方案有几个优点,客户端短期ID令牌可能每次都需要重定向机制来在到期时更新会话cookie:

因此,他们在这里暗示您希望从服务器管理会话及其生存期

第二条线索在

假设应用程序使用httpOnly服务器端cookies,则使用客户端SDK在登录页面上登录用户。生成Firebase ID令牌,然后通过HTTP POST将ID令牌发送到会话登录端点,在该端点处,使用Admin SDK生成会话cookie。成功后,应从客户端存储中清除状态

如果您查看示例代码,甚至可以使用
firebase.auth().setPersistence(firebase.auth.auth.persistence.None)将persistence显式设置为None以清除客户端的状态

因此,他们打算在客户端上没有超出初始身份验证的状态。他们明确地清除该状态,并期望得到一个
httponly
cookie,这样客户机就不能获取cookie(实际上只是ID令牌)并使用它来获取一个新的cookie

奇怪的是,没有清晰的方法刷新令牌客户端,但事实确实如此。您只能真正创建一个具有超长生存期的会话cookie,并在服务器上决定何时删除cookie或撤销刷新令牌等

因此,剩下另一个选项:在客户端管理状态。有些人只是在cookie中将ID令牌从客户端发送到服务器。SATE位于客户端上,客户端可以使用ID令牌来使用所有firebase功能。服务器可以验证用户身份并使用令牌等

这种情况应该会更好。如果服务器需要踢用户,那么它可以删除cookie并撤销刷新令牌(当然有点苛刻)


希望有帮助。另一个方案是构建自定义令牌,然后您就拥有了完全的控制权。

这就是您想要的吗@Dough Stevenson不,这不是我要找的,但我想这让我进一步解决了我的问题,所以谢谢你的推荐。你是说会话cookie声明不是最新的吗?您是从服务器更新配置文件,而不是使用updateProfile客户端API吗?@bojeil是的,它们不是最新的,因为我无法用新声明刷新它们。从我包含的代码片段中可以看到,我正在使用admin SDK和updateUser方法从服务器更新用户配置文件,但在我更新他的凭据后,他们不会反映在客户端中,如果我希望他们反映,我将不得不注销并再次登录,以获得一个新的会话cookie,该cookie具有新声明,因为我不知道如何在不注销和登录的情况下获取它。是的,不幸的是,如果不从客户端调用
updateProfile()
,您将无法刷新会话cookie,请刷新ID令牌,然后将ID令牌传递给服务器以交换新的会话cookie。也许您应该提交一个文件来支持会话cookie刷新(这也可以强制更新配置文件声明)。