4. Auth in a supergraph


To continue with our migration plan, we need to set up the router to run on the original server port (port 4000). Before we jump into the code to do that, let's take a look at how we're going to ensure that both the router and the monolith subgraph are set up to handle authentication and authorization correctly. In this lesson, we will:

  • Review how auth works in Airlock's monolithic architecture
  • Learn how to propagate authorization headers from the router to its subgraphs

How auth works in Airlock

Airlock has two types of users: hosts and guests. Each type of user can perform different kinds of actions.

When logged in as a host, you can:

  • create listings
  • manage bookings for your listings
  • write reviews about guests

When logged in as a guest, you can:

  • book places to stay
  • write reviews about the location and the host
  • manage your space credits

Currently, guest users are not allowed to create listings, and host users are not allowed to book places to stay.

With these business rules, our Airlock GraphQL API needs to control which users can see and interact with certain fields in the graph. This is where authentication and authorization come in.

  • Authentication is determining whether a given user is logged in, and subsequently determining which user someone is. (They are who they say they are.)
  • Authorization is determining what a given user has permission to do or see. (They're allowed to do what they're trying to do.)

Note: First time working with auth? Check out the Authentication & Authorization side quest for a closer look at how to verify user logins and permissions.

In Airlock, we're using HTTP headers, specifically the Authorization header with a Bearer token to provide our user credentials. The header looks something like this:

Authorization: Bearer user-1

Once the GraphQL server receives the incoming client operation, it retrieves the token from the request header and attempts to authenticate the user using the accounts service. After a successful authentication, the accounts service returns an object with userId and userRole, which the GraphQL server then adds to the context object that's available to every resolver.

Airlock uses field-level authorization. That means each resolver checks whether the logged-in user has permission to access that part of the graph. They do so using the userId and userRole properties from the context object.

Auth in a supergraph

In the original monolithic architecture, the GraphQL server received authorization headers from the client, did some logic to pull out the user information needed, then passed this information to its resolvers through the context object.

In the supergraph architecture, the story remains similar. The router will receive authorization headers from the client, then pass this information to its subgraphs. Each subgraph will do the same logic to pull out the user information and pass it over to their resolvers.

How does the router pass this information to its subgraphs? And what does the subgraph need to do to pass it to its resolvers? Let's dive into these questions and answer them in the story of "Auth in a supergraph"!

Note: We're going to focus on the story where the app uses HTTP headers and field-level authorization. Some parts of the story may change if your application uses different auth methods.

Authenticating the user

The story starts in the same way our GraphQL query journey does: with a request from the client to the server. This particular request includes an Authorization header that contains the logged-in user credentials.

HTTP request with auth header is sent from the browser to the router

The router receives this request and starts to create its query plan, using the supergraph schema as a reference to determine which subgraph to query for a particular field.

The router builds a query plan to resolve the request

Next, the router executes the query plan. It starts at the top, sending a request to the subgraph. Included with this request is the Authorization header that came from the client!

But how does the router know it should be passing along that header? It's all in the configuration!

Sending HTTP headers to subgraphs

When we start up the router, we have the option of passing in a configuration file, using the --config flag. This allows us to customize the router in many ways, such as configuring CORS (which we saw in Voyage I), introspection, and HTTP headers.

Using the config file, we'll tell the router to send the Authorization header to its subgraphs with every request. We'll see this configuration in action in the next lesson.

The config file provides instructions for which headers to send to the subgraphs

Over to the subgraph

When the subgraph receives the request from the router, it accesses the Authorization header from the request and attempts to authenticate the user.

If the attempt is unsuccessful, the subgraph stops and sends an AuthenticationError back to the router, which then sends it right back to the client.

The subgraph attempts to authenticate the user, returning an Authentication Error to the router if unsuccessful

If the authentication is successful, then the subgraph puts together an object containing user information and makes it available in its context. This user information object can be shaped in whatever way is going to be most useful to the subgraph's resolvers, and passed through the context object.

The subgraph resolves the operations the same way as any other GraphQL server: they use their resolvers and data sources to retrieve and populate the requested data. And they can use the user information in the context object to check against the specific business rules that guard their field and who has access to it!

The subgraph resolves the operation using its resolver functions and data sources

Back to the router

The subgraph sends back the requested data to the router, and the router continues with its query plan, eventually combining all those responses into a single JSON object.

Finally, the router sends the final JSON object back to the client. And that's the end of our operation's journey!

A doodle detailing the journey of authentication in a supergraph in its entirety


Which of the following options can be included as part of a router's config file?

Key takeaways

  • In Airlock, the client sends its request to the router, with the addition of an HTTP header called Authorization, which contains the current user's auth token.
  • The router can be customized with a configuration file, to pass along HTTP headers to its subgraphs.
  • The router passes the user's auth token to the subgraph, which checks whether or not the token is valid for login.
    • If the login is unsuccessful, the subgraph throws an AuthenticationError and sends it back to the router.
    • If the login is successful, the subgraph adds the current user's information to its context object, which is accessible by its resolvers for field-level authorization.

Up next

We've learned how to handle auth in a supergraph, so let's put everything together and implement this in code!