Search code examples
cookiesapollo-servergatewayapollo-federation

Apollo Gateway: Forward Subgraph `set-cookie` Header to Gateway to Client


I have a subgraph microservice that handles sessions. We store our sessions via cookies that the subgraph creates, and should set it via the set-cookie header. Only issue is my gateway does not seem to be forwarding the set-cookie header from the subgraph to the client.

Here is the code for my gateway

    const { ApolloServer } = require('apollo-server');
    const { ApolloGateway, RemoteGraphQLDataSource } = require('@apollo/gateway');
    const { readFileSync } = require('fs');
    
    const supergraphSdl = readFileSync('./gateway/supergraph.graphql').toString();
    
    class CookieDataSource extends RemoteGraphQLDataSource {
      didReceiveResponse({ response, request, context }) {
        const cookie = response.http.headers.get('set-cookie');
        console.log("Cookie:", cookie)

        return response;
      }
    }
    
    const gateway = new ApolloGateway({
      supergraphSdl,
      buildService({url}) {
        return new CookieDataSource({url});
      }
    });
    
    const server = new ApolloServer({
      gateway,
      cors: {
        origin: ["http://localhost:3000", "https://studio.apollographql.com"],
        credentials: true
      },
      csrfPrevention: true,
    });
    
    server.listen().then(({ url }) => {
      console.log(`🚀 Gateway ready at ${url}`);
    }).catch(err => {console.error(err)});

version info “@apollo/gateway”: “^2.1.2”, “apollo-server”: “^3.10.2”,

I can confirm that the subgraph is sending back a set-cookie header, however, it is not being passed through to the client.

Thank you!


Solution

  • I ended up resolving the issue by creating both a gateway datasource that added context value. Then, pass the header from the subgraph context value to the response header.

    import { GatewayGraphQLResponse, GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
    import { RemoteGraphQLDataSource } from '@apollo/gateway';
    import { ApolloServerPlugin, GraphQLRequestContext, GraphQLRequestListener } from '@apollo/server';
    
    interface ServerContext {
      passthrough_cookies?: string
    }
    
    export class CookieProcessorDataSource extends RemoteGraphQLDataSource {
      didReceiveResponse({response, context}: Required<Pick<GatewayGraphQLRequestContext<Record<string, any>>, 'request' | 'response' | 'context'>>): GatewayGraphQLResponse | Promise<GatewayGraphQLResponse> {
        context.passthrough_cookies = response.http?.headers.get('set-cookie');
        return response;
      }
    }
      
    export class CookieServerListener implements GraphQLRequestListener<ServerContext> {
      public willSendResponse({contextValue, response}: GraphQLRequestContext<ServerContext>): Promise<void> {
        if (contextValue.passthrough_cookies !== undefined) {
          response.http.headers.set('set-cookie', contextValue.passthrough_cookies);
        }
    
        return Promise.resolve()
      }
    }
    
    export class CookieServerPlugin implements ApolloServerPlugin<ServerContext> {
      async requestDidStart() {
        return new CookieServerListener();
      }
    }