Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Federation] Feature Request: fire willSendRequest on gateway initialization #2833

Closed
calebfaruki opened this issue Jun 12, 2019 · 2 comments

Comments

@calebfaruki
Copy link

Context

In our microservice architecture, we have Authorization headers to gate access to our services. In this case, our gateway service needs to pass along expected authorization headers to our federated service.

Problem

On initialization of the gateway service, the willSendRequest callback will only fire when a GraphQL request is executed, which makes sense. But there is not configuration option available to send an initial authorization header.

Therefore, we can't use the global context object on the federated service to throw an AuthenticationError.

Source Code

From the gateway app:

const startGateway = async () => {
  const gateway = new ApolloGateway({
    debug: ENV !== 'prod',
    serviceList: [
      { name: 'federated-svc', url: `${FEDERATED_SVC}/graphql` },
    ],
    buildService: ({ name, url }) => {
      return new RemoteGraphQLDataSource({
        url,
        willSendRequest ({ request, context }) {
          // Get JWT payload, resign with same secret, and pass to federated service.
          if (context.user) {
            const jwt = jwtSign(context.user, JWT_SECRET_KEY)
            request.http.headers.set('Authorization', `JWT ${jwt}`)
          } else {
            request.http.headers.set('Authorization', ROUTE_AUTH)
          }
        },
      })
    },
  })
  let server, gatewayConfig
  try {
    gatewayConfig = await gateway.load()
    server = new ApolloServer({
      ...gatewayConfig,
      cache,
      dataSources,
      engine: { apiKey: ENGINE_API_KEY },
      tracing: true,
      context,
    })
    server.applyMiddleware({ app })
  } catch (err) {
    console.error(err)
  }
}

On the federated service, this is ideally how we would configure the server. It's important to note the business logic of the global context object here.

const schema = buildFederatedSchema([{ typeDefs, resolvers }])

const url = new URL(`redis://${REDIS_URL}:${REDIS_PORT}/${REDIS_DATABASE}`).toString()

const cache = new RedisCache({ url })

try {
  const server = new ApolloServer({
    schema,
    cache,
    debug: true,
    dataSources,
    engine: { apiKey: ENGINE_API_KEY },
    tracing: true,
    context ({ req }) {
      if (!(req.user || req.headers.authorization === ROUTE_AUTH)) {
        throw new AuthenticationError('Not authenticated')
      }
      return { user: req.user, req: req }
    },
  })
  server.applyMiddleware({ app })
} catch (err) {
  console.error(err)
  throw err
}

However, if you use this, the gateway service will receive the Not authenticated error thrown by the context object of the federated service.

The intuition is that a request to a service behind our gateway should carry an Authorization header with a known secret or a JWT in a way that each service knows the secret to validate the authenticity of the JWT that's passed. On initialization of the Gateway, there is no user context to pass along, so an expected secret should be supported.

Please let me know if you need more detail to understand this request.

Despite that, got it to work! Loving this federation pattern so far.

@jbaxleyiii
Copy link
Contributor

@calebfaruki we are working on a design that lets you pull the service config with a lot more control! We want to separate willSendRequest from fetching service capabilities. This was a major source of confusion in stitching and we think not doing detch on startup is probably the better long term solution. We will have more details on this very soon!

@trevor-scheer
Copy link
Member

Closing via #3120.

Noting that this PR conflicts directly with what @jbaxleyiii said above - I moved forward with that PR, but was missing the context mentioned by James to decide against this. For now, the mentioned PR unlocks a viable solution for this.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants