【问题标题】:Cookie not set, even though it is in response headers. Using express-session cookies未设置 Cookie,即使它位于响应标头中。使用快速会话 cookie
【发布时间】:2020-11-11 02:35:14
【问题描述】:

问题:

尝试使用express-session 在登录时设置cookie,但我认为我遗漏了一些明显的东西。登录 POST 请求的响应包括 Set-Cookie。我还将Access-Control-Allow-OriginAccess-Control-Allow-Headers 设置为通配符,如下所示: https://i.stack.imgur.com/XS0Zv.png

但我们看到,在浏览器存储中(尝试使用 Firefox 和 Chrome),什么都没有。 As shown here

我目前正在按如下方式设置我的快速会话(请参阅帖子末尾的完整代码。添加 sn-p 以便于阅读):

app.use(session({
        genid: () => { return uuidv4(); },
        store: new MongoStore({ mongooseConnection: mongoose.connection }),
        secret: process.env.SESSION_SECRET,
        resave: false,
        saveUninitialized: true,
        cookie: {
          httpOnly: true,
          secure: false,
          sameSite: true,
        }
      })
    );

然后在我验证用户登录后,我尝试通过以下方式设置 userId:

req.session.userId = user.id;

可能的相关信息

  • 这些会话已成功存储在 Mongo 中,如您所见 here,这让我相信我至少正确地生成了会话。现在我可能完全错了……
  • 我的后端正在localhost:8000 上运行,通过:app.listen(8000);
  • 我的客户端在http://localhost:3000/ 上运行
  • 尽量不要将 Apollo GraphQL 用于学习目的

到目前为止我尝试过的事情:

  • resavesaveUnitialized 的不同组合。
  • 删除cookie参数。
  • 停止设置用户ID
  • 重启浏览器和服务器
  • 查看了相关的堆栈溢出帖子

请指教!甚至关于如何调试这个或我可以查看的其他内容的想法也会非常有帮助!

相关代码

app.js

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const {v4: uuidv4} = require('uuid');

const graphqlSchema = require('./graphql/schema/index');
const graphqlResolvers = require('./graphql/resolvers/index'); 

const app = express();
const path = '/graphql';

app.use(bodyParser.json());

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'POST,GET,OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', '*');
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  next();
});

mongoose
  .connect(`mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@cluster0.ccz92.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`,
    { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }
  )
  .then(() => {
    app.use(session({
        genid: () => { return uuidv4(); },
        store: new MongoStore({ mongooseConnection: mongoose.connection }),
        secret: process.env.SESSION_SECRET,
        resave: false,
        saveUninitialized: true,
        cookie: {
          httpOnly: true,
          secure: false,
          sameSite: true,
        }
      })
    );
    app.use(path, graphqlHTTP({
      schema: graphqlSchema,
      rootValue: graphqlResolvers,
      graphiql: true,
    }));

    app.listen(8000);
  })
  .catch(err => {
    console.log(err);
  });

graphql/resolvers/auth.js

const argon2 = require('argon2');
const jwt = require('jsonwebtoken');

const User = require('../../models/user');

module.exports = {
  createUser: async args => {
    try {
      const existingUser = await User.findOne({
        email: args.userInput.email
      });
      if (existingUser) {
        throw new Error('User exists already.');
      }
      const hashedPassword = await argon2.hash(
        args.userInput.password,
        12
      );
      const user = new User({
        email: args.userInput.email,
        password: hashedPassword,
        loggedIn: true
      });
      const result = await user.save();
      const token = jwt.sign(
        { userId: result.id, email: result.email },
        process.env.JWT_KEY,
        { expiresIn: '1h' }
      );
      return {
        userId: result.id,
        token: token,
        tokenExpiration: 1
      };
    } catch (err) {
      console.log("error in resolvers/auth.js");
      throw err;
    }
  },
  login: async (args, req) => {
    const { userId } = req.session;
    if (userId) {
      console.log("found req.session");
      return User.findOne({ _id: userId });
    }
    console.log("looking for user with ", args.userInput.email);
    const user = await User.findOne({ email: args.userInput.email });
    console.log("found user");
    if (!user) {
      throw new Error("User does not exist!");
    }
    user.loggedIn = true;
    user.save();
    const isEqual = await argon2.verify(user.password, args.userInput.password);
    if (!isEqual) {
      throw new Error ("Password is incorrect!");
    }
    console.log("setting session.userId");
    req.session.userId = user.id;
    return { ...user._doc, password: null};
  },
  logout: async (args, req) => {
    if (!req.isAuth) {
      throw new Error('Unauthenticated');
    }
    try {
      const result = await User.findOneAndUpdate(
        { _id: req.userId },
        { loggedIn: false },
        { new: true },
      );
      return { ...result._doc, password: null };
    } catch (err) {
      console.log("logout error", err);
      throw(err);
    }
  },
};

【问题讨论】:

    标签: node.js express cookies express-session express-graphql


    【解决方案1】:

    结果证明这是一个 CORS 问题。我没有意识到这个港口意味着不同的起源。在这种情况下,我的客户端在 3000,而我的服务器在 8000。

    鉴于 CORS 的性质,在获取时我需要在客户端中包含 credentials(cookie、授权标头或 TLS 客户端证书):

    fetch(config.url.API_URL, {
          method: 'POST',
          body: JSON.stringify(requestBody),
          headers: {
            'Content-Type': 'application/json'
          },
          credentials: "include",
        })
    

    这将告诉用户代理始终发送 cookie。

    然后服务器端我需要将Access-Control-Allow-Credentials 设置为这样:

    res.setHeader('Access-Control-Allow-Credentials', true);
    

    这将允许浏览器将响应(具有 cookie)公开给前端 Javascript 代码。

    由于我们使用的是凭据,因此我们需要指定 Access-Control-Allow-HeadersAccess-Control-Allow-Origin

    res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
    
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-01
      • 2021-10-22
      • 1970-01-01
      • 2019-06-10
      • 2022-01-23
      • 2016-08-08
      • 2013-12-26
      相关资源
      最近更新 更多