【问题标题】:Express-session does not set cookie?Express-session 没有设置cookie?
【发布时间】:2021-11-18 19:46:29
【问题描述】:

我跟随Ben Awad's 13-hour Fullstack React GraphQL TypeScript Tutorial,在设置登录cookie时遇到了墙(大约1:50:00)。

我认为我已成功连接到 redis,设置 express-session 并设置 req 类型,但在 graphql 沙箱中,我在 Inspect->Application 中看不到我的 cookie(名为“qid”)。

index.ts

import { MikroORM } from "@mikro-orm/core";
import { __prod__ } from "./constants";
import microConfig from "./mikro-orm.config";
import express from "express";
import { ApolloServer } from "apollo-server-express";
import { buildSchema } from "type-graphql";
import { HelloResolver } from "./resolvers/hello";
import { PostResolver } from "./resolvers/post";
import { UserResolver } from "./resolvers/user";
import redis from "redis";
import session from "express-session";
import connectRedis from "connect-redis";

const main = async () => {
  const orm = await MikroORM.init(microConfig);
  await orm.getMigrator().up();

  const app = express();

  const RedisStore = connectRedis(session);
  const redisClient = redis.createClient();

  app.use(
    session({
      name: "qid",
      store: new RedisStore({
        client: redisClient,
        disableTouch: true,
      }),
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 365 * 10,
        httpOnly: true,
        sameSite: "none",
        // secure: __prod__,
      },
      saveUninitialized: false,
      secret: "dfhfdjkgfkbjktzkzf",
      resave: false,
    })
  );

  app.use(function (req, res, next) {
    res.header(
      "Access-Control-Allow-Origin",
      "https://studio.apollographql.com"
    );
    res.header("Access-Control-Allow-Credentials", "true");
    next();
  });

  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [HelloResolver, PostResolver, UserResolver],
      validate: false,
    }),
    context: ({ req, res }) => ({ em: orm.em, req, res }),
  });

  await apolloServer.start();
  apolloServer.applyMiddleware({
    app,
    cors: {
      credentials: true,
      origin: new RegExp("/*/"),
    },
  });

  app.listen(4000, () => {
    console.log("server started on port 4000");
  });
};

main();

types.ts

import { EntityManager, IDatabaseDriver, Connection } from "@mikro-orm/core";
import { Request, Response } from "express";
import { Session, SessionData } from "express-session";

export type MyContext = {
  em: EntityManager<any> & EntityManager<IDatabaseDriver<Connection>>;
  req: Request & {
    session: Session & Partial<SessionData> & { userId: number };
  };
  res: Response;
};

和我的 userResolver (user.ts)

import { User } from "../entities/User";
import { MyContext } from "../types";
import {
  Arg,
  Ctx,
  Field,
  InputType,
  Mutation,
  ObjectType,
  Query,
  Resolver,
} from "type-graphql";
import argon2 from "argon2";

@InputType()
class UsernamePasswordInput {
  @Field()
  username: string;

  @Field()
  password: string;
}

@ObjectType()
class FieldError {
  @Field()
  field: string;

  @Field()
  message: string;
}

@ObjectType()
class UserResponse {
  @Field(() => [FieldError], { nullable: true })
  errors?: FieldError[];

  @Field(() => User, { nullable: true })
  user?: User;
}

@Resolver()
export class UserResolver {


  @Mutation(() => UserResponse)
  async login(
    @Arg("options", () => UsernamePasswordInput) options: UsernamePasswordInput,
    @Ctx() { em, req }: MyContext
  ): Promise<UserResponse> {
    const user = await em.findOne(User, { username: options.username });
    if (!user) {
      return {
        errors: [
          {
            field: "username",
            message: "username does not exist",
          },
        ],
      };
    }
    const valid = await argon2.verify(user.password, options.password);
    if (!valid) {
      return {
        errors: [
          {
            field: "password",
            message: "incorrect password",
          },
        ],
      };
    }

    req.session.userId = user.id;

    return {
      user,
    };
  }
}

我尝试按照 graphql 沙盒的要求设置 res.headers,但仍然无济于事。非常感谢您的帮助,谢谢!

【问题讨论】:

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


    【解决方案1】:

    好的,我不确定发生了什么,但我似乎解决了这个问题。

    我的想法是:GraphQL Playground 已停用,并且 localhost:port/graphql 现在重定向到 Apollo GraphQL Sandbox 到不同的 url,我的猜测是 cookie 没有被传输到这个位置,但 cookie 设置在 localhost。

    所以有一种方法可以强制 Apollo 继续使用 Playground,方法是添加:

    import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core";
    
    
      const apolloServer = new ApolloServer({
        ...,
        plugins: [
          ApolloServerPluginLandingPageGraphQLPlayground({
            // options
          }),
        ],
      });
    

    这样 Playground 就会出现,你可以设置

      "request.credentials": "include",
    

    在设置中,瞧,cookie 显示在 localhost:port。

    我希望这可以帮助解决这个问题的任何人 - 但是我仍然不确定这是一个正确的解决方案。

    【讨论】:

      【解决方案2】:

      将旧的 Playground 添​​加为插件可能可行,但由于他们说它已被弃用,如果你想让它与新的 Apollo Studio 一起使用,我是这样做的:

      我在初始化应用后立即添加了这三行:

        app.set("trust proxy", !process.env.NODE_ENV === "production");
        app.set("Access-Control-Allow-Origin", "https://studio.apollographql.com");
        app.set("Access-Control-Allow-Credentials", true);
      

      我的会话配置如下所示:

      const RedisStore = connectRedis(session);
      const redisClient = redis.createClient();
      
      app.use(
        session({
          saveUninitialized: false,
          store: new RedisStore({ client: redisClient }),
          cookie: {
            maxAge: 1000 * 60 * 60 * 24 * 365 * 1, // 1 year
            httpOnly: true,
            sameSite: "none",
            secure: true, // if true, studio works, postman doesn't; if false its the other way around
          },
          name: "qid",
          secret: "keyboard cat",
          resave: false,
        }),
      );
      

      然后,转到 Apollo Studio,转到 Connection Settings -> Edit -> Include Cookies(这个真的很难找到):

      确保在每次登录请求时都发送此标头:x-forwarded-proto: https

      【讨论】:

        猜你喜欢
        • 2021-02-14
        • 1970-01-01
        • 2017-06-20
        • 2016-08-17
        • 1970-01-01
        • 2019-08-02
        • 2021-02-08
        • 1970-01-01
        • 2019-05-18
        相关资源
        最近更新 更多