3.4: Authentication
Last updated
Last updated
Understand the flow of typical authentication processes and how they use JWTs and cookies
Understand how to implement secure authentication with Auth0
Authentication is both simple and complex. The high-level concept of authenticating a user, then saving that user's authentic "badge" in their browser for subsequent requests is simple. But the lower-level concepts of how to authenticate and how to save that badge in the browser can be complex.
At Rocket Academy we will focus on the higher-level concepts of authentication, and let students dig deeper into the lower-level concepts themselves if time permits and students are interested. We will use Auth0 to implement authentication in our API servers, the industry standard for plug-and-play authentication libraries, comparable to Firebase Auth but with friendlier docs.
At a fundamental level, all authentication involves 3 steps.
User enters auth info such as username and password
If auth info verified, backend sends user's browser a cookie that contains proof of authentication, typically either a JSON web token (JWT) or a session ID that cannot be forged. Browsers store cookies and send them to relevant websites, thereby proving authentication.
Authentication can be challenging because it involves many moving parts. Our server application must store auth info such as passwords and secret keys securely. Our cryptographic algorithm and proof of authentication must be industry-standard. Any bugs in logic or implementation can result in costly hacks.
This is why Rocket recommends we use a plug-and-play, industry-standard auth solution such as Auth0, and only write our own lower-level authentication logic when there is a strong business need and we have experts to test the robustness of our implementation.
Luckily for us, companies such as Auth0 and Firebase have developed plug-and-play auth solutions that are both secure and easy to use. We will use Auth0 for backend authentication because Auth0 has clearer documentation for this use case.
Create Auth0 account if we haven't already
Yes, we will be the one coding
No, we do not need advanced settings
Create Application
Choose Single Page Web Application
Choose React
We should then be directed to a quickstart page like the following, populated with our app's specific domains and IDs. No need to implement anything for now, but read through to understand the high-level process.
Note application domain and client ID in Application Settings in the Auth0 dashboard. We will need to include these in our app to communicate with Auth0.
Configure callback and logout URLs to allow redirects back to our apps after logging in or out with Auth0
Configure allowed web origins to allow auth tokens (JWTs that serve as proof of authentication) to automatically refresh periodically after users have logged in to prevent auto-logout when auth tokens expire
Surround App
component with Auth0Provider
component to enable Auth0 in our apps. Note Auth0Provider
uses React context underneath.
Add login to app with loginWithRedirect
function from useAuth0
React hook. Login is as simple as calling the function, letting users login in Auth0's interface, then redirecting back to our app.
Ditto with the logout
function also from the useAuth0
React hook. returnTo: window.location.origin
tells Auth0 to redirect to the root URL of the current browser window after logging out.
Retrieve logged-in user profile information through the user
property of the useAuth0
hook. user
contains properties such as picture
, name
and email
.
That's all there is to logging in and out with Auth0 in our frontend! Let's now learn how to send that auth info to our backends for our users to access our backends securely.
Now that we've enabled login in our React apps, we need to learn how to pass an access token from Auth0 to our backends to verify authentication.
Our frontends authenticate with our backends via an access token that we retrieve from Auth0 on our frontends, send to our backends in a request, and verify using an Auth0 library on our backends.
To retrieve this access token on our frontends, we need to pass additional audience
and scope
props to the Auth0Provider
component.
audience
is an identifier for our API server that we set in Auth0. This is typically the URL of our API server.
scope
is a property we define in Auth0 that allows us to only allow access to specific backend routes from specific frontend apps. In our case we will keep scope simple and allow access to all protected routes from our frontend.
Retrieve access token with getAccessTokenSilently
method from useAuth0
React hook
Use getAccessTokenSilently
to retrieve access token just before we send an API request via Axios. In Auth0's example they use Fetch to send requests, but the concept is the same.
Pass audience
and scope
again to getAccessTokenSilently
to get a token for the correct domain and scope
We need to include the access token in an "Authorization" request header with the format shown in this guide. Our API server will need the header in this format to process the access token.
Voila! We are now ready to set up authorisation in our backends for protected routes!
When defining the Auth0Provider, there has been a slight alteration! Please pass in the property authorizationParams to include the redirectUri, audience and scope. Similar to the code below:
We will implement authentication in our backend first because there are certain credentials to set up for our API server that we will need to use in our frontends later.
Some routes in our backend will be protected, for example routes to access user data or manipulate data. Based on the logged-in user, backends can decide what data to expose to that user and record changes by that user.
Create an API in the Auth0 dashboard and give it an identifier, typically a URL that identifies our API. We will use this API identifier as an audience
in our frontends when retrieving an access token to communicate with our backends
There is no need to understand RS256 and what a private/public keypair is at the moment, other than knowing they are industry-standard security mechanisms.
We can ignore scope for now because our apps will be simple and all users should be able to access all features
Install Auth0's express-oauth2-jwt-bearer
middleware to verify authentication on our routes
Initialise the middleware in the root index.js
file of our Express app with our API's identifier as audience
and the issuerBaseURL
provided by Auth0 (login to Auth0 while viewing this guide to see it)
Insert checkJwt
middleware in routes that require authentication, and add checkScopes
middleware in routes that also require specific scopes.
Great job! We now have authentication and secure access to our APIs!
Hopefully this gave us a clear high-level overview of what steps we need to implement Auth0 authentication in our frontends, how to send auth info in requests and protect our backends using Auth0 authorisation. We will get more hands-on practice with Auth0 in the upcoming hands-on auth exercise.
Please checkout the finished frontend code below. Note that you will need to setup a application on Auth0 that you integrate with the application.
Frontend code is in this repository, ensure that you're on the auth
branch if you want to test the code on your machine you will need to install the dependencies with the command npm install
after the installation you can run the application with npm run dev
. To view the rendered page open a browser and navigate to http://localhost:5173 Note, you will need to create your own .env and create your own Auth0 application online with the appropriate setup. To test it with a backend please checkout this repository, ensure that you're on the auth
branch if you want to test the code on your machine you will need to install the dependencies with the command npm install
after the installation, then implement you .env
. If you've not setup the database previously, run your migrations and seeders and once this is completed you can run the application with node index.js
.