简体   繁体   English

Express-session 没有设置cookie?

[英]Express-session does not set cookie?

I'm following along with Ben Awad's 13-hour Fullstack React GraphQL TypeScript Tutorial and encountered a wall during the login cookie setting (aprx at 1:50:00).我正在关注Ben Awad 的 13 小时 Fullstack React GraphQL TypeScript 教程,在登录 cookie 设置期间遇到了一堵墙(大约 1:50:00)。

I think I successfully connected to redis, set express-session and set req type but in graphql sandbox I don't see my cookie (named 'qid') at Inspect->Application.我想我已成功连接到 redis,设置 express-session 并设置 req 类型,但在 graphql 沙箱中,我在 Inspect->Application 中看不到我的 cookie(名为“qid”)。

index.ts索引.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类型.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;
};

and my userResolver (user.ts)和我的 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,
    };
  }
}

I tried setting up res.headers as graphql sandbox is asking but still to no avail.我尝试将 res.headers 设置为 graphql 沙盒询问但仍然无济于事。 Would appreciate any help, thank you!将不胜感激任何帮助,谢谢!

Okay I'm not sure what is happening, but I seemingly solved the issue.好的,我不确定发生了什么,但我似乎解决了这个问题。

My idea is that: GraphQL Playground is retired and localhost:port/graphql now redirects to Apollo GraphQL Sandbox to a different url and my guess is that the cookies do not get transfered to this location but the cookie is set at localhost.我的想法是:GraphQL Playground 已退役,并且 localhost:port/graphql 现在重定向到 Apollo GraphQL Sandbox 到不同的 url,我的猜测是 cookie 没有传输到这个位置,但 cookie 设置在 localhost。

So there is a way you can force Apollo to still use the Playground by adding:所以有一种方法可以通过添加以下内容来强制 Apollo 仍然使用 Playground:

import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core";


  const apolloServer = new ApolloServer({
    ...,
    plugins: [
      ApolloServerPluginLandingPageGraphQLPlayground({
        // options
      }),
    ],
  });

And this way Playground shows up and you can set这样 Playground 就会出现,你可以设置

  "request.credentials": "include",

in the settings and voila the cookie shows up at localhost:port.在设置中,瞧,cookie 显示在 localhost:port 中。

I hope this helps anyone with this issue - however I'm still not exactly sure that this is a right solution.我希望这可以帮助解决这个问题的任何人 - 但是我仍然不确定这是一个正确的解决方案。

Adding the old playground as a plugin probably works but, since they say it is being deprecated, if you want to make it work with the new Apollo Studio, here is how I managed to do it:将旧的 Playground 添​​加为插件可能可行,但由于他们说它已被弃用,如果你想让它与新的 Apollo Studio 一起工作,我是这样做的:

I added these three lines right after initializing the app:我在初始化应用程序后立即添加了这三行:

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

Here is how the configuration of my session looks like:这是我的会话配置的样子:

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

Then, over to Apollo Studio, go to Connection Settings -> Edit -> Include Cookies (this one was really hard to find):然后,转到 Apollo Studio,转到 Connection Settings -> Edit -> Include Cookies(这个真的很难找到):

在此处输入图像描述

Make sure to send this header with every request to login: x-forwarded-proto: https确保在每次登录请求时都发送此标头: x-forwarded-proto: https

spent some time on this one.花了一些时间在这个上。 Try this combined solution:试试这个组合解决方案:

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 { PostResolver } from "./resolvers/Post";
import { UserResolver } from "./resolvers/User";
import session from "express-session";
import connectRedis from "connect-redis";
import { createClient } from "redis";

const main = async () => {
  try {
    const orm = await MikroORM.init(microConfig);
    orm.getMigrator().up();
    const app = express();
    app.set("trust proxy", process.env.NODE_ENV !== "production"); //a little fix here from another users codes--- actually think this made it works
    app.set("Access-Control-Allow-Origin", "https://studio.apollographql.com");
    app.set("Access-Control-Allow-Credentials", true);
    let redisClient = createClient({ legacyMode: true });
    redisClient.connect().catch(console.error);
    let RedisStore = connectRedis(session);
    const cors = {
      credentials: true,
      origin: "https://studio.apollographql.com",
    };
    app.use(
      session({
        name: "qid",
        store: new RedisStore({ client: redisClient as any, disableTTL: true }),
        cookie: {
          maxAge: 1000 * 60 * 60 * 24 * 365 * 10,
          httpOnly: true,
          secure: true,
          sameSite: "none",
        },
        saveUninitialized: false,
        secret: "keyboardcaxt",
        resave: false,
      })
    );
    const apolloServer = new ApolloServer({
      schema: await buildSchema({
        resolvers: [PostResolver, UserResolver],
        validate: false,
      }),
      context: ({ req, res }) => ({ em: orm.em, req, res }),
    });

    await apolloServer.start();
    apolloServer.applyMiddleware({ app, cors });
    app.listen(4000, () => {
      console.log("EXPRESS SERVER IS RUNNINGG");
    });
  } catch (error) {
    console.log(error, "ERRR");
  }
};

main();

Also dont forget to this setup and hard-reset your https://studio.apollographql.com/sandbox/ .也不要忘记此设置并硬重置您的https://studio.apollographql.com/sandbox/ And this: add ENV to your root还有这个:将 ENV 添加到您的根目录

Then you should be ready to go.然后你应该准备好 go。

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

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