简体   繁体   English

未设置 Cookie,即使它位于响应标头中。 使用快速会话 cookies

[英]Cookie not set, even though it is in response headers. Using express-session cookies

Problem:问题:

Trying to set the cookie on login using express-session , but think I'm missing something obvious.尝试使用express-session在登录时设置 cookie,但认为我遗漏了一些明显的东西。 The response to the login POST request includes Set-Cookie.登录 POST 请求的响应包括 Set-Cookie。 I've also set the Access-Control-Allow-Origin and Access-Control-Allow-Headers to wildcards as shown here: https://i.stack.imgur.com/XS0Zv.png我还将Access-Control-Allow-OriginAccess-Control-Allow-Headers设置为通配符,如下所示: https://i.stack.imgur.com/XS0Zv.png

But we see that in the browser storage (tried with Firefox and Chrome) there is nothing.但我们看到在浏览器存储中(尝试使用 Firefox 和 Chrome)没有任何内容。 As shown here如此处所示

I'm currently setting my express-session as follows (refer to end of post for full code. Adding snippet for easier read):我目前正在按如下方式设置我的快速会话(请参阅帖子末尾的完整代码。添加代码片段以便于阅读):

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,
        }
      })
    );

Then after I've verified the user is getting logged in, I try to set the userId via:然后在我验证用户正在登录后,我尝试通过以下方式设置 userId:

req.session.userId = user.id;

Possibly Relevant Info可能相关信息

  • These sessions are successfully getting stored in Mongo as you can see here , which makes me believe that I'm at least generating the sessions correctly.正如您在此处看到的,这些会话已成功存储在 Mongo 中,这让我相信我至少正确地生成了会话。 Now I could be totally wrong here...现在我在这里可能完全错了......
  • my backend is running on localhost:8000 via: app.listen(8000);我的后端通过: app.listen(8000);localhost:8000上运行;
  • my client is running on http://localhost:3000/我的客户端在http://localhost:3000/上运行
  • trying not to use Apollo GraphQL for learning purposes尽量不要将 Apollo GraphQL 用于学习目的

Things I've tried so far:到目前为止我尝试过的事情:

  • different combinatons of resave , saveUnitialized . resavesaveUnitialized的不同组合。
  • remove the cookie parameter.删除cookie参数。
  • stop setting userId停止设置用户 ID
  • restarting browser and servers重启浏览器和服务器
  • Looked at relevant stack overflow posts查看了相关的堆栈溢出帖子

Please advise!请指教! Even ideas on how to debug this or what other things I can look at would be immensely helpful!甚至关于如何调试这个或我可以查看的其他内容的想法也会非常有帮助!

Relevant Code相关代码

app.js应用程序.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 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);
    }
  },
};

So it turned out to be a CORS issue.所以原来是 CORS 问题。 I didn't realize that the port would mean a different origin.我没有意识到这个港口意味着不同的起源。 In this case my client is at 3000 and my server is at 8000.在这种情况下,我的客户端位于 3000,而我的服务器位于 8000。

Given the CORS nature, in the client I need to include credentials (cookies, authorization headers, or TLS client certificates) when I'm fetching:鉴于 CORS 的性质,在获取时我需要在客户端中包含credentials (cookie、授权标头或 TLS 客户端证书):

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

This will tell the user agent to always send cookies.这将告诉用户代理始终发送 cookies。

Then serverside I need to set Access-Control-Allow-Credentials to be true as such:然后服务器端我需要将Access-Control-Allow-Credentials设置为 true,如下所示:

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

This will allow the browser to expose the response (which has the cookie) to the frontend Javascript code.这将允许浏览器向前端 Javascript 代码公开响应(具有 cookie)。

Since we are using credentials, we will need to specify Access-Control-Allow-Headers and Access-Control-Allow-Origin由于我们使用凭据,我们需要指定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');

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM