5. Configuring the router
7m

Overview

Let's put together what we've learned about how to implement auth in a . In this lesson, we will:

  • Configure the to pass HTTP headers to s
  • Set up the to receive the headers and authenticate the user
  • Test out a that needs authentication and authorization using
Diagram of step 2 of the migration plan. Step 2 is creating a router running on the original port that is connected to the registry.

Service check

Before moving on, let's make sure all of our processes are still running!

  • One terminal should be running npm start, to start the monolith on port 4001.
  • Another should be running npm run launch, to start our services in the monolith directory on ports 4010 and 4011.

✏️ Downloading the router

  1. Open a new terminal window and navigate to the router directory.

  2. Download the by running the following command in the terminal:

    router
    curl -sSL https://router.apollo.dev/download/nix/v1.46.0 | sh

    Note: Visit the official documentation to explore alternate methods of downloading the Router.

  3. Now when we check the contents of our router directory, we'll see that we have a new file also called router!

    📦 router
    ┣ 📄 router
    ┣ 📄 router-config.yaml
    ┗ 📄 supergraph-config.yaml
  4. There's one last change we should make. We initially stored the environment variables for our in our .env file in the monolith directory so that we could easily refer back to them. We'll continue to reference the stored in .env as we work with the , so let's move that file from the monolith directory into the router directory.

    📦 router
    ┣ 📄 .env
    ┣ 📄 router
    ┣ 📄 router-config.yaml
    ┗ 📄 supergraph-config.yaml

The router's config file

Before we start up the , let's investigate its config file. Inside of router-config.yaml, we'll find that there's already one configuration option specified:

router-config.yaml
include_subgraph_errors:
all: true # Propagate errors from all subgraphs

By default, errors are omitted from logs for security reasons. The setting we're referencing in the configuration file, include_subgraph_errors, is a setting that will allow the to share the details of any errors that occur as it communicates with . By setting the all key to true, we're telling the that we want to know about errors that occur in all of our .

This is great for development purposes, because it will help us troubleshoot anything that goes wrong along the way!

✏️ Adding Authorization headers

Note: There are other methods of implementing authentication in a , such as using authorization in the schema, using a coprocessor or using a JWT Authentication plugin. In this course, we'll be focusing on authentication in a 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.

Let's add another configuration option to our 's router-config.yaml file.

We want to tell the to send the Authorization header to its with every request.

router-config.yaml
headers:
all:
request:
- propagate:
named: "Authorization"

Let's break down what these new lines do.

  • The headers key is where we set all of our rules for HTTP headers.

  • all indicates that the rules nested underneath will apply to all the . We could specify which ones using the subgraphs key, but we know that for Airlock we'll need to pass it to all of them.

  • request specifies that the included headers should apply to requests the receives.

  • Finally, we want to pass the Authorization header to all the , so we'll use the propagate key, then indicate the name of the header with named: 'Authorization'.

Note: You can consult the Apollo documentation to learn more about the other ways of specifying which HTTP headers to propagate.

With our all set up to pass these authentication headers to its , let's make sure the subgraph is ready to receive these headers and pass them along to its own !

Setting up the subgraph for auth

The good news is that the monolith is already set up for auth!

Remember back when the monolith was a standalone ? Well, it was already taking in the Authorization header, retrieving the user token and authenticating it using the accounts service!

Let's see this logic in the code.

Open up the monolith/index.js file, our monolith . Scrolling down to where we started up the server, we can find the context property and review that logic:

monolith/index.js
context: async ({ req }) => {
const token = req.headers.authorization || "";
const userId = token.split(" ")[1]; // get the user name after 'Bearer '
let userInfo = {};
if (userId) {
const { data } = await axios.get(`http://localhost:4011/login/${userId}`).catch((error) => {
throw AuthenticationError();
});
userInfo = { userId: data.id, userRole: data.role };
}
const { cache } = server;
return {
...userInfo,
dataSources: {
bookingsDb: new BookingsDataSource(),
reviewsDb: new ReviewsDataSource(),
listingsAPI: new ListingsAPI({cache}),
accountsAPI: new AccountsAPI({cache}),
paymentsAPI: new PaymentsAPI({cache}),
},
};
},

With this logic already in place, we don't have to do any additional work in our index.js file!

Running the router with config

Let's get our running.

  1. In a new terminal, navigate to the router directory.

  2. To start the , we'll type in our APOLLO_KEY and APOLLO_GRAPH_REF. (Remember, you can find these inside of your .env file!) Then, start the with ./router, and finally add the --config flag with the path to the router-config.yaml file.

    router
    APOLLO_KEY=<APOLLO_KEY> APOLLO_GRAPH_REF=<APOLLO_GRAPH_REF> ./router --config router-config.yaml

    We should see output messages in the terminal, with our running at 127.0.0.1:4000.

Remember port 4000 was where the original monolith was running? Now we've successfully replaced it with the ! Clients don't have to do anything extra to change their endpoints, they can keep as usual.

👩🏽‍🔬 Testing the Authorization headers

Let's give it a try! We can head back over to the Explorer in Studio to test out a for the .

We'll try out a that requires an authenticated and authorized user: retrieving a host's listings. This query needs you to be logged in as a host.

Using Explorer, let's build a to retrieve a host's listings. For each listing, we'll ask for the title, costPerNight, description, photoThumbnail, numOfBeds, and locationType.

query GetHostListings {
hostListings {
title
costPerNight
description
photoThumbnail
numOfBeds
locationType
}
}

Let's run the , and… uh oh! We get back an AuthenticationError with no logged-in user 😱

After a brief moment of panic, let's think about what we're missing.

We've just set up the server-side handling of authorization headers, but we haven't actually sent those headers with our request! So our server is trying to authenticate the user sending the request, but it can't find them and it returns the appropriate error.

Let's go ahead and add those headers. In the bottom panel of the Explorer, open the Headers tab.

Click New header and set the header key as Authorization. Set the value as Bearer user-1. We know that user-1 is a host!

http://studio.apollographql.com
Screenshot of Studio showing where the Headers panel is on the bottom of the screen and where to add the authorization headers
Authorization: Bearer user-1

Let's run the again! We get the list we're asking for with all the we need.

We get a ForbiddenError that says only hosts have access to listings - we're logged in as a guest so that's great, our server is working as it's supposed to!

Diagram of step 2 of the migration plan, checked off and completed. Step 2 is creating a router running on the original port that is connected to the registry.

Practice

config.yaml
headers:
all:
request:
- propagate:
named: "airlock-cookie"
In the above config.yaml file, what does the propagate key tell the router to do?

Key takeaways

  • To pass down authorization headers from the to its , we need to set up the router's config file.
    • In the config file, we can set the headers property and use the propagate property.
  • A can access the authorization (and other request) headers from the through its ApolloServer constructor context property.
  • We can use Apollo Explorer's Headers panel to set authorization headers for our request.

Up next

We've checked off another task in our migration plan, and we're ready to move on to split this even further.

Previous

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.