Подписка GraphQL, websocket, nodejs, экспресс-сессия

Я использую 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 и тд ...

Что я могу сделать, так это проанализировать файлы cookie из webSocket.upgradeReq.headers.cookie в ловушке onConnect жизненного цикла, но мне это кажется очень хакерским.

Код:

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 (другие темы очень хорошо документированы!).

Где я делаю не так?


person Fred Hors    schedule 11.09.2018    source источник
comment
как ты это решил?   -  person Nux    schedule 01.10.2019


Ответы (4)


Я знаю это старое, но сам столкнулся с этой проблемой, и как я ее решил, я запустил webSocket.upgradeReq.session через промежуточное ПО для сеанса.

введите описание изображения здесь

Затем в onConnect:

введите описание изображения здесь

И у вас есть доступ к вашей сессии

person TSlegaitis    schedule 07.09.2020

Это то, что ! использовать

  //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 ?? '';
}

Благодарим за все ответы здесь

person Nux    schedule 17.03.2021

См. Этот комментарий: https://github.com/expressjs/cookie-session/issues/117#issuecomment-452046225 от dougwilson, чтобы получить значения сеанса.

Если вы просто хотите получить содержимое сеанса (и не записывать новый сеанс - что было бы невозможно без разрешения), вы, вероятно, могли бы просто напрямую использовать модуль cookie, который этот модуль использует под капотом для прочтите файл 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 ()

person Ricardo Seromenho    schedule 07.01.2019

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

Похоже, это предполагает, что вы можете передать необходимые данные cookie на стороне клиента для получения в контексте на сервере.

Передайте параметры для подключения к веб-сокету: https://www.apollographql.com/docs/react/advanced/subscriptions.html#authentication.

person Chris    schedule 25.01.2019