hero

How to integrate Auth0 authenticated Frontend applications with API services

Following on from the previous blog post on How to setup Single Page Applications with Auth0 in the local environment without CORS issues, I noticed another area that is very vague in the official docs and thought I'd share my finding in this post. It turns out Auth0 provided everything we needed just didn't tell us how to use it.

This blog post assumes you have a working Frontend app already. If you are not quite there yet, be sure to check out How to setup Single Page Applications with Auth0 in local environment without CORS issues if you want some tips!

Auth0's user object

If you are not aware already, on the Frontend side it is possible to request a user profile from Auth0 and it will return something similar to the following. Assuming, of course, you've got the authentication working already.

{
    email: "hao@email.com"
    email_verified: true
    family_name: "Dev"
    given_name: "Hao"
    locale: "en-GB"
    name: "Hao Dev"
    nickname: "Nickname"
    picture: "https://image.com/fake-profile-pic.png"
    sub: "google-oauth2|1924810923701923"
    updated_at: "2021-07-29T17:40:14.223Z"
}

The important thing to note here is the sub property, this is the equivalent of something like user.id we typically use to refer to a unique user in our database. If we want to store custom data about this user or user inputted data then we would save them against the sub value and later we can retrieve it using the same thing.

Attach access token to API requests

According to Call Your API Using the Client Credentials Flow section of the doc, we need to attach the access token as a bearer token in all requests headers. Which makes sense as this will allow the Backend to authenticate and verify the user without blindly trusting what the Frontend says.

Here's the node example given in the above documentation:

const axios = require("axios").default;

const options = {
  method: 'GET',
  url: 'https://myapi.com/api',
  headers: {'content-type': 'application/json', authorization: 'Bearer ACCESS_TOKEN'}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Receiving access token on Backend

On the Backend, we will need to intercept all requests that require authentication. Luckily, Auth0 has made this part very easy to find (see screenshot below).

Auth0 API instructions

The code example given on the page is unique to each API, here is an example to illustrate to anyone who hasn't set up an API yet.

const express = require('express');
const app = express();
const jwt = require('express-jwt');
const jwks = require('jwks-rsa');

const port = process.env.PORT || 8080;

const jwtCheck = jwt({
      secret: jwks.expressJwtSecret({
          cache: true,
          rateLimit: true,
          jwksRequestsPerMinute: 5,
          jwksUri: 'https://example-api-dev.eu.auth0.com/.well-known/jwks.json'
    }),
    audience: 'https://an-example.com',
    issuer: 'https://example-api-dev.eu.auth0.com/',
    algorithms: ['RS256']
});

app.use(jwtCheck);

app.get('/authorized', function (req, res) {
    res.send('Secured Resource');
});

app.listen(port);

In this example, it shows us that we need to use two libraries here express-jwt and jwks-rsa to verify the access/bearer token passed from the Frontend's side. Then using jwtCheck() function as a middleware, the request will only reach app.get('/authorized') if the JWT token passes the authentication check.

Now we have used the access token that came as part of the request and verified it with Auth0 and made sure it is valid and haven't been tampered with.

Getting unique identifier on Backend

Apparently, jwtCheck verification automatically populates req.user if everything is successful. After checking out express-jwt documentation, it seems that it is doing all that work for us.

It is very confusing because that isn't made clear anyway in the Auth0 docs and I went through a lot of them. I was lost and confused until I saw this line in one of the example codebases which helped me to figure it out.

app.use(jwtCheck);

app.get('/authorized', function (req, res) {
        // if we can reach here, then req.user is already populated
        console.log(user.req);

    res.send('Secured Resource');
});

The console log will print out the same user object shown on the Frontend side.

{
    email: "hao@email.com"
    email_verified: true
    family_name: "Dev"
    given_name: "Hao"
    locale: "en-GB"
    name: "Hao Dev"
    nickname: "Nickname"
    picture: "https://image.com/fake-profile-pic.png"
    sub: "google-oauth2|1924810923701923"
    updated_at: "2021-07-29T17:40:14.223Z"
}

Keeping some endpoints open

Is it possible to not use app.use(jwtCheck); and lock down all of the endpoints? Yes, as far as I am aware there are a couple of ways to achieve this.

  1. Authenticate each endpoint/route individually
  2. Define app.use(jwtCheck); after the endpoints that don't require authentication

Although in many of Auth0's examples I read that it is possible to define JWT verification on each endpoint, I never actually got the first method working myself. The following snippet is supposed to work.

// ❌ didn't work for me

app.get('/authorized', jwtCheck, function (req, res) {
    res.send('Secured Resource');
});

app.get('/unauthorized', function (req, res) {
    res.send('Public Resource');
});

app.get('/another-authorized', jwtCheck, function (req, res) {
    res.send('Another Secured Resource');
});

In the end, I went with the second approach, which works for me with no problem. It is unfortunate since the first solution would have been a lot neater.

// ✅ worked for me

app.get('/unauthorized', function (req, res) {
    res.send('Public Resource');
});

app.use(jwtCheck);

app.get('/authorized', function (req, res) {
    res.send('Secured Resource');
});

Final words

I hope this information was useful, I was stuck on this for a long time because I couldn't find any information on it.

PS: If you want to know more about authorisation checks (e.g. use Auth0's permission system) check out Server + API: Auth0 Configuration and Validate Access Tokens. Although they don't give you the code you need, it is a good place to start.