question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Missing Details/Unexpected behaviour Authenticating over Websocket using graphql-ws.

See original GitHub issue

Bug Report

The section of the documentation on authentication over websocket says that the data returned from forRoot.subscriptions.['subscription-transport-ws' || 'graphql-ws'].onConnect callback will be acessible in the forRoot.context callback via connection.context. However that doesn’t seem to be the case at least for graphql-ws. connection is undefined in context regardless of what is returned from onConnect.

Irrespective of what was returned from forRoot.subscriptions.[graphql-ws'].onConnect, I was only able to access the connectionParams parsed from the client via context directly like this

{
  context: ({ connectionParams, connection }) => { } // connection will evaluate to undefined while connectionParams hold necessary authentication parameters. 
}

I am not sure if this is the intended behaviour and was missed out on the documentation or a bug but I am sure someone else will experience this soon enough.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:2
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
chrismllrcommented, Oct 14, 2021

I am seeing a similar issue, but for subscriptions-transport-ws configuration. It actually appears that the context method is not being called for subscriptions, even after the initial onConnect ack.

I’ve verified that the onConnect is called once at the beginning, and the correct { user } payload is being returned; but the context method is never called.

This may be an issue with how I’ve set up the gql module, so i will include my config below:

    const config = GraphQLModule.forRootAsync({
      imports: [ConfigModule, AuthModule, UsersModule],
      inject: [ConfigService, UsersService, 'TOKEN_VERIFIER'],
      useFactory: (
        configService: ConfigService,
        usersService: UsersService,
        tokenVerifier: any
      ) => {
        const authorize = async (accessToken: string) => {
          const token = await tokenVerifier.verify(accessToken);
          return usersService.userFromToken(token);
        };

        return {
          autoSchemaFile: path.join(process.cwd(), 'src/schema.gql'),
          sortSchema: true,
          cors: true,
          installSubscriptionHandlers: true,
          playground: false,
          plugins: [ApolloServerPluginLandingPageLocalDefault()],
          subscriptions: {
            'subscriptions-transport-ws': {
              path: '/subscriptions',
              onConnect: async (connectionParams: any) => {
                if (typeof connectionParams?.accessToken !== 'string') {
                  return false;
                }

                const user = await authorize(connectionParams?.accessToken);
                return { user };
              },
            },
            'graphql-ws': {
              path: '/subscriptions',
              onConnect: async ({ connectionParams }) => {
                if (typeof connectionParams?.accessToken !== 'string') {
                  return false;
                }

                const user = await authorize(connectionParams?.accessToken);
                return { user };
              },
            },
          },
          context: ({ connection, req, res }) => {
            // Only called after standard queries and mutations, not subscription connects
            return connection
              ? {
                  req: {
                    ...req,
                    ...connection.context,
                  },
                  res: res,
                }
              : { req: req, res: res };
          },
        };
      },
    });
1reaction
chrismllrcommented, Oct 14, 2021

I believe I was able to resolve the issue:

On this line within subscriptions-transport-ws: https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/server.ts#L316

The result of the onConnect cb is merged with the context for subsequent messages, and in my case, my current-user decorator was accessing context.req.user, so my change was simple:

// From
const user = await authorize(connectionParams?.accessToken);
return { user };

// To
const user = await authorize(connectionParams?.accessToken);
return { req: { user } };

That said, the context method is still never called for subscriptions, which is OK, because the subscriptions-transport-ws lib is doing this merge you. But this might be worth updating in the documentation, if it is indeed the case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Subscriptions in Apollo Server
This article uses the graphql-ws library to add support for subscriptions to Apollo Server 4. We no longer recommend using the previously documented ......
Read more >
GraphQL over WebSockets – The Guild
Coherent, zero-dependency, lazy, simple, server and client implementation of the new, security first, GraphQL over WebSocket Protocol.
Read more >
graphql-ws
Start using graphql-ws in your project by running `npm i graphql-ws`. There are 212 other projects in the npm registry using graphql-ws.
Read more >
How to detect disconnect and reconnect for subscription ...
log("websocket reconnected!!")) There are more events you can listen to. But these events are sufficient to implement fetching missed chat messages. With these ......
Read more >
Building a real-time WebSocket client - AWS AppSync
The client establishes a WebSocket connection with the AWS AppSync real-time ... endpoint with the query string, using graphql-ws as the WebSocket protocol....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found