Token authentication with React and Apollo Client— a custom hook example
Authentication is often a pain for beginners (and experimented!) developers.
The goal of this article is to show a basic authentication system on the client side (a React App) with Apollo Client.
Disclaimer : this article does not cover authentication on the backend!
In this article we will cover
- managing your authentication token with react-cookies
- passing your auth token in your headers
- login & logout
- encapsulating the logic in reusable hooks
As backend, we will use Strapi, which provides a ready to use auth system to test our frontend. You can use any backend that works with a token.
The full repo with Strapi and React is here : https://github.com/ovrsea/react-apollo-strapi-auth-example
If you want to test it, go in /backend, do yarn install && yarn start
, and in /frontend, do yarn install && yarn start
Global architecture
Below is shown the architecture that we want to implement:
There are two “authentication data”: the auth token itself and the user data. The auth token authenticates the queries, and is thus mandatory to consider the user authenticated. The user data is a good way to “check” the authentication token : if it fails, the user has to re-login for example.
We want to give access to our App if and only if we have a token and the user query works with this token.
Managing your auth token
The first step is to manage our auth token. In this example, our auth token is stored in the cookies of the browser. It allows to persist the session even if the user leaves and comes back.
First, we have to wrap our App with a CookiesProvider
Then, we create a hook that allows to:
- get the token
- save the token
- remove the token
Encapsulating in a custom hook will allow us to access our token easily across our App. Here is our code:
Passing your auth token to your queries
Once we have a token (which we don’t have for now! 😁), we have to put the token in the headers of our requests. A classic authentication system is to put the token in the “authorization” header, under the form "Bearer <myToken>"
In our case, we have to use an Apollo Link in order to do that. A “link” is a piece of software that “modifies” the request and passes it along. For example, it can update headers, log errors, etc.
Below is the full code to configure our Apollo Client:
What do we have here ?
First, we have the normal configuration of an Apollo Client : http-link + cache.
Then, we created an authMiddleware that “injects” the token in our authorization header. That’s it!
Getting an auth token
At this point, if we have an auth token, we can query our backend. The thing is, we don’t have one!
In order to fetch an auth token, we have to send our login and password to the server, that will respond with a token that we have to save for further use. This is done by using the “login” mutation in our case :
What’s interesting here is that we composed hooks. Remember our useAuthToken hook? We have now created a hook that combines useMutation and useAuthToken. This hook calls the login mutation, and if it succeeds, saves the results in our token store (the cookies!)
We can now call this hook from a basic form (using react-hook-form here) :
Fetching the user and protecting our App
As stated before, we also need to fetch our user once we have the token stored in our cookies. Below is a code that works for a Strapi backend :
Everything is in now in place, all we have to do is to decide what to serve : authentication form or protected page? In this example, we created an AuthGate, in charge of deciding :
We are now all set!
Going further : logout
Logging out is now super easy, we will create a custom hook again. Logging out will:
- clear apollo client cache
- remove the authToken from the cookies
We can use it in our PrivatePage for example:
Going further : remove the token before login
Sometimes, having a token in the headers for the login mutation can trigger bugs (this is the case for Strapi, if the token is invalid for example). To avoid this, we can remove the token before triggering the mutation. We slightly adapt our login hook:
You can also go further by:
- not re-instantiating an Apollo Client every time App renders(use useCallback or useMemo)
- logging out on the server too
- etc.
Overall, authentication is a never ending topic. This article is a good starting point for a React + Apollo web app, but let us know in the comments how you improved this basic schema and your use case!
Happy coding!
Interesting links on the internet :
https://kentcdodds.com/blog/authentication-in-react-applications