2. Authentication: Identifying users
5m

Overview

In the last lesson, we learned that authentication is the process of determining whether a given user is logged in, and then determining which user someone is. In other words, authentication is checking that a user is who they say they are.

Airlock uses a simplified login process: users can log in as either a host account or a guest account. You can test out the login process at http://localhost:3000/login (or on the deployed Airlock demo app).

In this lesson, we will:

  • Learn how to authenticate users by using an HTTP header and the context object.

🕵️‍♂️ Authentication

There are many ways to provide credentials for authentication, such as HTTP headers, session cookies, or JSON web tokens.

Airlock uses HTTP headers. Let's take a closer look at how it works.

On the client side

Recall that are sent from the client to the server in the form of an HTTP request. In Airlock, every operation includes an HTTP Authorization header with a Bearer token to provide authentication credentials. The header looks something like this:

Authorization: Bearer user-1

Note: If you're curious, you can see the client code that adds this header in client/src/index.js (where the authLink gets set up).


Setting up client-side authentication is out of scope for this side quest, so we won't be going into this in detail. For more information, you can check out the Apollo Client docs on authentication.

In the example above, user-1 is the token that indicates which user is making the request. We can see that the token is simply "user-1", which is the exact ID of the user. In production environments, this token typically takes the shape of a temporary, encoded string, which would be generated by your user management system (like Auth0 or Okta). But for the purposes of this course, we'll stick with plain user IDs, for simplicity when testing.

On the server side

Once the receives the incoming client , it retrieves the token from the request header and attempts to authenticate the user. Let's take a look at how Airlock breaks down the steps in this process:

  1. The retrieves the Bearer token from the client request's Authorization header.
  2. If a Bearer token exists, the server passes it to the accounts service, which tries to log in the corresponding user.
  3. If the user login succeeds, the accounts service sends back an object with the user's profile data. Then the adds the user's id and role to the context object that's available to every .

From there, each can use these user properties for authorization purposes, to determine what data the user has permission to access. (But we'll get to that in the next lesson!)

The code for this is in the server/index.js file, in the context property of the ApolloServer initialization:

server/index.js
// ...
context: async ({ req }) => {
// 1) Retrieve the Bearer token from the request's Authorization header
// (Note the lowercase "a" in authorization,
// because all headers are transformed to lowercase)
const token = req.headers.authorization || '';
// Get the user token after "Bearer "
const userId = token.split(' ')[1]; // e.g. "user-1"
// Initialize the userInfo object where the user's id and role will be stored
// with a successful authentication
let userInfo = {};
if (userId) {
// 2) Authenticate the user using the accounts API endpoint
const { data } = await axios.get(`http://localhost:4011/login/${userId}`).catch((error) => {
throw AuthenticationError();
});
// 3) After a successful login, store the user's id and role
// in the userInfo object,
// which will be passed to `context` below for the resolvers to use
userInfo = { userId: data.id, userRole: data.role };
}
// for RESTDataSource classes
const { cache } = server;
// Below is the `context` object resolvers will have access to
return {
...userInfo,
dataSources: {
bookingsDb: new BookingsDataSource(),
reviewsDb: new ReviewsDataSource(),
listingsAPI: new ListingsAPI({cache}),
accountsAPI: new AccountsAPI({cache}),
paymentsAPI: new PaymentsAPI({cache}),
},
};
},
// ...

Practice

Which of these describe how our Airlock app authenticates users?

Key takeaways

  • One way clients can authenticate users is by passing an HTTP Authorization request header with the it sends to the server.
  • Authentication logic can be written within the context property of the ApolloServer constructor, so that user information will be available to every .

Up next

Now that we've seen how Airlock handles authentication, the next step is authorization. In the next lesson, we'll look at how Airlock uses the user info from the context object to decide whether a user has permission to perform a certain .

Previous