Overview
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 a single GraphQL server.
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 contextValue
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 contextValue
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 contextValue
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.
Note: There are other methods of implementing authentication in a supergraph, such as using authorization directives in the schema, using a coprocessor or using a JWT Authentication plugin. In this course, we'll be focusing on authentication in a subgraph using header propagation. You can combine the strategies to handle a number of authentication requirements and practice "defense-in-depth". For more information on these approaches, check out this Apollo technote.
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"!
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.
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.
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.
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.
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 contextValue
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 contextValue
object to check against the specific business rules that guard their field and who has access to it!
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!
Practice
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
contextValue
object, which is accessible by its resolvers for field-level authorization.
- If the login is unsuccessful, the subgraph throws an
Up next
We've learned how to handle auth in a supergraph, so let's put everything together and implement this in code!
Share your questions and comments about this lesson
Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.