【问题标题】:Why is the req.cookies.session undefined? Firebase + Node + express为什么 req.cookies.session 未定义? Firebase + 节点 + 快递
【发布时间】:2019-03-17 08:49:23
【问题描述】:

我正在尝试分配一个生成会话 cookie 以换取提供的 ID 令牌。 Here 是我关注的文档。

这是我的客户端登录代码:

firebase.auth().signInWithEmailAndPassword(email, password)
    .then(function(user) {
        firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
            return sendToken(idToken);
        });
    })
    // .then(() => {
    //     return firebase.auth().signOut();
    // })
    .then(() => {
        window.location.assign('/member');
    })
    .catch(function(error) {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        alert(errorMessage);
    });

我的 send sendToken() 将 idToken 发布到服务器:

function sendToken(idToken) {
console.log("Posting " + idToken);
var xhr = new XMLHttpRequest();
var params = `token=${idToken}`;
xhr.open('POST', "/login", true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
return new Promise(function(resolve, reject) {
   xhr.onreadystatechange = function() {//Call a function when the state changes.
        if (xhr.readyState == 4 && xhr.status == 200) {
            resolve();
        } else if (xhr.readyState == 4 && xhr.status != 200) {
            reject("Invalid http return status");
        }
    }
   return xhr.send(params);
});
}

在服务器上,我返回了一个会话 cookie:

app.post('/login', (req, res) => {
if (req.body.token) {
   const idToken = req.body.token.toString();
  // Set session expiration to 1 day.
   const expiresIn = 60 * 60 * 24 * 1 * 1000;
   return firebase.auth().createSessionCookie(idToken, {expiresIn}).then((sessionCookie) => {
     const options = {maxAge: expiresIn, httpOnly: true, secure: true};
     res.cookie('session', sessionCookie, options);
     res.end(JSON.stringify({status: 'success'}));
  })
  .catch((error) => {
  res.status(401).send('UNAUTHORIZED REQUEST!');
 });
}
return res.status(400).send("MISSING TOKEN");
});

我还设置了一个中间件,用于在服务器提供会员信息之前验证会话 cookie:

function authVerification(req, res, next){
 const sessionCookie = req.cookies.session || '';
 // Verify the session cookie. In this case an additional check is added to 
 detect
 // if the user's Firebase session was revoked, user deleted/disabled, etc.
 return firebase.auth().verifySessionCookie(
 sessionCookie, true /** checkRevoked */).then((decodedClaims) => {
   console.log("decoded claims: " + decodedClaims);
   next();
   // serveContentForUser('/profile', req, res, decodedClaims);
 }).catch(error => {
   // Session cookie is unavailable or invalid. Force user to login.
   res.send(error);
 });
}

但是,当我在登录后尝试获取会员页面时:

app.get("/member", authVerification, (req, res) => {
  res.send("member page");
});

我不断收到来自 authVerification 的错误:

代码:“身份验证/参数错误”, 消息:“解码 Firebase 会话 cookie 失败。确保传递了代表会话 cookie 的整个字符串 JWT。有关如何检索会话 cookie 的详细信息,请参阅 https://firebase.google.com/docs/auth/admin/manage-cookies。”

谁能指出我正确的方向。提前谢谢!

【问题讨论】:

    标签: node.js firebase express firebase-authentication session-cookies


    【解决方案1】:

    quickstart-nodejs repo 中的解决方案。

    enter link description here

    您必须添加 cookie-parserbody-parser。以下是我使用 Firebase Cloud Functions 解决的方法:

    const admin = require("firebase-admin");
    const functions = require("firebase-functions");
    const next = require("next");
    const cors = require("cors");
    const express = require("express");
    const cookieParser = require("cookie-parser");
    const bodyParser = require("body-parser");
    
    const dev = process.env.NODE_ENV !== "production";
    const app = next({ dev, conf: { distDir: "next" } });
    const handle = app.getRequestHandler();
    
    admin.initializeApp({
      credential: admin.credential.cert("Service account key"),
      databaseURL: "Path to database"
    });
    
    const server = express();
    server.use(cors({ origin: true }));
    server.use(bodyParser.json());
    // Support URL-encoded bodies.
    server.use(bodyParser.urlencoded({
      extended: true
    }));
    // Support cookie manipulation.
    server.use(cookieParser());
    // Attach CSRF token on each request.
    server.use(attachCsrfToken('/', 'csrfToken', (Math.random()* 100000000000000000).toString()));
    
    
    function attachCsrfToken(url, cookie, value) {
      return function(req, res, next) {
        if (req.url === url) {
          res.cookie(cookie, value);
        }
        next();
      }
    }
    
    server.post("/login", (req, res) => {
      if (req.body && req.body.idToken) {
        const idToken = `${req.body.idToken}`;
        const expiresIn = 60 * 60 * 24 * 5 * 1000;
        admin.auth().createSessionCookie(idToken, { expiresIn }).then((sessionCookie) => {
          const options = { maxAge: expiresIn, httpOnly: true, secure: true };
          res.cookie("session", sessionCookie, options);
          res.end(JSON.stringify({ sessionCookie }));
        }, error => {
          res.status(401).send(error);
        });
      } else {
        res.status(401).send("Token empty");
      }
    });
    
    server.post("/profile", (req, res) => {
      if (req.cookies && req.cookies.session) {
        const sessionCookie = `${req.cookies.session}`;
        admin.auth().verifySessionCookie(
          sessionCookie, true /** checkRevoked */).then((decodedClaims) => {
          res.end(JSON.stringify({ decodedClaims }));
        }).catch(error => {
          res.status(401).send(error);
        });
      } else {
        res.status(401).send("Session empty");
      }
    });
    
    exports.next = functions.https.onRequest((req, res) => {
      if (req.method === "POST") {
        if (!req.path) req.url = `/${request.url}`;
        return server(req, res);
      }
    
      return app.prepare().then(() => handle(req, res));
    });
    

    客户端没有什么特别的。您可以使用 isomorphic-unfetch、axios 或 jQuery。

    【讨论】:

    • @francis,感谢分享。这无疑帮助我更好地理解了整个概念。然而。我仍然无法设置cookie。我已经修改了我的代码,但仍然无法设置浏览器 cookie。
    • @francis - 我想澄清一下,我正在 LocalHost 上尝试它并设置了 cookie 选项 - 安全:false。感谢您的帮助!
    • 你在客户端用什么?
    • @francis,感谢您为我指明了正确的方向。我能够让它工作。按照 GitHub 存储库中的设置进行操作。我没有在客户端上使用任何特定的框架。它是简单的 JavaScript、HTML 和 CSS。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多