GraphQL订阅,websocket,nodejs,Express会话

时间:2018-09-11 16:34:19

标签: express websocket graphql session-cookies apollo-server

我正在使用Apollo Server 2(,但这个问题不仅是Apollo )和Express.js vanilla(带有apollo-server-express)。

快速会话机制外,订阅也都可以正常工作。

问题:

我正在使用 cookie-session https://github.com/expressjs/cookie-session,但是我认为 express-session 中间件是一样的),并且当我的浏览器启动与服务器的新连接,ApolloServer onConnect钩子没有req属性,也没有req.session等……

我能做的是在webSocket.upgradeReq.headers.cookie生命周期钩子中解析来自onConnect的cookie,但是在我看来,这很hacky。

代码:

const { ApolloServer } = require('apollo-server-express')

const typeDefs = require('../src/graphql/types')
const resolvers = require('../src/graphql/resolvers')
const models = require('../src/models')

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req, connection }) => {
    // connection exists only on webSocket connection
    if (connection) {
      return {
        currentUser: connection.context.currentUser // <-- I NEED THIS!
      }
    }
    // if not a (webSocket) connection it is a "default" HTTP call
    return {
      models,
      currentUser: { id: req.user.id }
    }
  },
  subscriptions: {
    onConnect: (connectionParams, webSocket) => {
      // "connectionParams" is from the client but I cannot use it because cookies are HTTP-Only
      // I can retrieve cookies from here: "webSocket.upgradeReq.headers.cookie" but then I need to parse them which seems a bit hacky to me
      // return { currentUser: req.user.id } // <-- I NEED THIS (req.user.id doesn't exists)!
    }
  }
})

module.exports = apolloServer

我在Apollo Server Docs网站上找不到任何东西(对于其他文献记录非常丰富!)。

我在哪里做错了?

4 个答案:

答案 0 :(得分:1)

我知道这个年代很久,但是我自己遇到了这个问题,而我如何解决这个问题是我通过会话中间件运行了webSocket.upgradeReq.session。

enter image description here

然后进入onConnect:

enter image description here

您就可以访问会话

答案 1 :(得分:1)

这是什么!使用

  //Session middleware before resolvers
  const sessionParser = session({
    store,
    secret: config.get('keys.session_secret'),
    //...The rest config here
    }
  });
  const apolloServer = new ApolloServer({
    schema,
    context: ({req, res, connection}: TheContext): TheContext => {
      return {req, res, redisStore: store, connection}; //connection is must here
    },
    subscriptions: {
      onConnect: (_params, _ws, ctx) => {
        return new Promise((resolve, reject) => {
          const req = ctx.request as express.Request;
          const res = ({} as any) as express.Response;

          //Parsing session
          sessionParser(req, res, (_: any) => {
            const session = req.session as any;

            //Getting userId which was stored during login
            const userId = session.userId;

            if (!userId) {
              reject(new AppError('You are not logged in'));
            }

            //This will be available in subscription via connection as
            //something.connection.context.userId
            resolve({userId});
          });
        });
      }
    }
  });

我像这样使用 TypeGraphql

  @Subscription(() => Post, {
    topics: ({context}) => `${Topic.DELETED_POST}:${parseUserId(context)}`,
    description: 'To be used in requests list and not for individual request'
  })
  deletedPost(@Root() post: Post) {
    return post;
  }

这就是我如何访问存储在会话中的 userId

export function parseUserId(obj: any): string {
  return obj.connection.context.userId ?? '';
}

感谢所有答案here

答案 2 :(得分:0)

请参阅以下评论:dougwilson的https://github.com/expressjs/cookie-session/issues/117#issuecomment-452046225以获取会话值。

  

如果您只想获取会话内容(而不写回新会话-如果没有res就无法实现),则可能直接使用cookies模块,该模块在以下模块中使用读取cookie并验证签名的引擎盖。一个可能的例子:

const Cookies = require('cookies')

// …

const cookies = new Cookies(req, null, { keys }) const session =
JSON.parse(Buffer.from(cookies.get('session'),'base64').toString('utf8'))

onConnect返回您需要在subscribe()上使用的任何内容

答案 3 :(得分:0)

https://www.apollographql.com/docs/apollo-server/features/subscriptions.html#Authentication-Over-WebSocket

这似乎建议您可以在客户端传递所需的cookie数据,以在服务器上的上下文中接收。

传入用于websocket连接的参数:https://www.apollographql.com/docs/react/advanced/subscriptions.html#authentication