Cloud-based authentication and authorization platforms—sometimes known as IDaaS, or identity as a service — are an expanding area of cloud tooling, and it’s easy to see why. App security is difficult and error-prone, and virtually every project requires it. The ability to offload much of the work to a dedicated and proven service is enticing.

is an up-and-coming provider of authentication and authorization services (and ). In this article, you will see how to incorporate Auth0 log-in capabilities into a app with a Node.js/Express back end, serving a straight JS front end, and then use the authenticated user info (via JWT) to show/hide UI information and secure RESTful endpoints.

Create a simple Node.js/Express app

First you will set up a simple Express app. Begin by typing npm init from the command line. You can provide whatever values you like for the project name and so on. The final code from the project is available . Note that this sample app is intended to highlight the security elements in a simple and condensed manner, so many production-necessary features like error handling and configuration files have been left out.

Next, install Express from the same directory where you ran init by running npm install express.

In your code editor of choice, add a server.js file in the root directory and put the contents of Listing 1 in it.

Listing 1. Basic server.js file

  const express = require('express');
  const app = express();

app.get('/api/open', function(req, res) {
  console.log("/api/open");
  res.json({
    message: "Open Endpoint"
  });
});

app.get('/api/members-only', function(req, res){
  console.log("/api/members-only")
  res.json({
    message: 'Members Only Endpoint'
  });
})

app.get('/api/protected', function(req, res) {
  console.log("/api/protected")
  res.json({
    message: 'Protected Endpoint'
  });
});

app.listen(3000);
console.log('Listening on http://localhost:3000');

Listing 1 sketches out what we’re shooting for: three API endpoints, one open, one requiring an active log-in, and one requiring log-in and a specific permission.

. When you sign up, Auth0 will create a default “System API” for you. This is a special API that is one-per-tenant, and gives you access to the Auth0 platform. The public keys (in this case jwks for RS256) are exposed via this API.

Next we will create an API in the Auth0 system. This is a representation of your real-world API (the endpoints we want to secure) that allows you to apply Auth0 capabilities. Click the “Create API” button, which will open the screen you see in Figure 1.

IDG

Figure 1.

For the name field, you can use anything that is memorable. For identifier, you should use a URL, but you don’t have to expose the URL or even own it — it is just an identifier to which you will refer in your code. Of course, in a real-world project, you would use your actual domain name or other owned resource. For the last field on this form, you can leave the algorithm as RS256.

Use the Auth0 API

The public key for the RS256 pair is now hosted for you at the URL with the format https://[your_domain].auth0.com/.well-known/jwks.json. You can find the detail for your new API by clicking the “settings” link by it. Notice the identifier you provided now has the form https://[your_domain].us.auth0.com/api/v2/. You will see both of these URLs in action shortly.

The next bit of housekeeping you have to do is to define permissions. In this case, you want a permission that will be required for accessing the protected endpoint we created earlier. From the settings page, select the “Permissions” tab. Create a read:protected permission and hit the “Add” button.

In a moment you will apply this permission to the protected endpoint.

Use the Express auth middleware

You are going to use Express middleware to enforce the permission policy. Go ahead and install the dependencies in Listing 3, which includes an Express JWT (JSON web token), a JSON web key, and Express JWT authorization extensions, respectively. Remember that JWT is an encrypted token that carries the auth information. It will be used to communicate between the front end, the back end, and the Auth0 platform.

Listing 3. Add auth dependencies

npm install --save express-jwt jwks-rsa express-jwt-authz

Add checkJwt to server.js, along with the necessary imports, as seen in Listing 4. Notice that there are elements (in square brackets) that you will fill in with your details.

Listing 4. Securing the endpoint

//...
const jwt = require('express-jwt');
const jwtAuthz = require('express-jwt-authz');
const jwksRsa = require('jwks-rsa');
//...
const checkJwt = jwt({
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: `https://[YOUR SYSTEM API DOMAIN].us.auth0.com/.well-known/jwks.json`
  }),

  audience: '[THE IDENTIFIER FROM YOUR API]',
  issuer: [`https://[YOUR SYSTEM API DOMAIN].us.auth0.com/`],
  algorithms: ['RS256']
});
var options = { customScopeKey: 'permissions'};  // This is necessary to support the direct-user permissions
const checkScopes = jwtAuthz([ 'read:protected' ]);
//...

app.get('/api/members-only', checkJwt, function(req, res){
  console.log("/api/members-only")
  res.json({
    message: 'Members Only Endpoint'
  });
})

app.get('/api/protected', checkJwt, checkScopes, function(req, res) {
  console.log("/api/protected")
  res.json({
    message: 'Protected Endpoint'
  });
});

In broad strokes, what is happening here is that we create an Express middleware checkJwt that will check for a valid JSON web token. This is configured to use the information from the Auth0 API you created earlier.

Notice both issuer and jwksUri point to your System API account, which was created for you when you signed up. Again, there is one System API account per tenant, not per API. This account provides the keys (the JSON Web Key Set, in this case) to sign the auth information for specific APIs.

The audience field will refer to the identifier for the API you created, not the System API account.

Finally, notice that there is also checkScopes applied to the protected endpoint. This checks for the read:protected permission.

Check your progress

At this point, if you return to the browser and click the “Members Only API” (or “Protected API”) button, the server will respond with an error:

UnauthorizedError: No authorization token was found.

That’s a good sign that things are starting to work.

Create an Auth0 client application

Just as you created an Auth0 API to model your back-end app, you’ll now create and configure a client, or consumer, of your secured endpoints. Again, Auth0 calls them SPAs (they used to be called just “Clients,” and still are in some of the Auth0 docs). Go to the Auth0 dashboard, and in the left-hand menu select “Applications -> Applications,” just above the API link you used before when configuring the server.

Now select the “Create Application” button. Give it a name (perhaps call it “Client” to distinguish it from the back-end app) and make sure to select “SPA” as the type. Hit “Create.”

Now open the client application by selecting from the list. Herein you’ll find the info you need to set up the client side of our test app: the domain and client ID. Make note of this information; we’ll use it in just a moment.

Configure the callback, logout, and Web Origin URLs in App Settings

First however, as seen in Figure 2, add the localhost address (http://localhost:3000) for the dev app to the allowed callbacks. This lets Auth0 know it can use your development URL for these purposes.

Figure 2. Adding localhost to client config

IDG

Figure 2.

Build out the client auth

Now return to the app code and add the Auth0 SDK to the client, in index.html. In this case, we’ll use the CDN. Add the following to the header of the file:

<script src="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></script>

Now we can tie in the auth. Begin by wiring up the log-in and log-out buttons. The handlers for them are seen in Listing 5.

Listing 5. Log-in and log-out handlers

const configureClient = async () => {
        auth0 = await createAuth0Client({
          domain: "[YOUR SYSTEM API URL].us.auth0.com",
          client_id: "[YOUR CLIENT ID]",
          audience: "[YOUR API IDENTIFIER]" // The backend api id
        });
      }
const login = async () => {
        await auth0.loginWithRedirect({
          redirect_uri: "http://localhost:3000"
        });
      };
      const logout = () => {
        auth0.logout({
          returnTo: window.location.origin
        });
      };

With Listing 5, first you configure your Auth0 client using the settings information noted earlier. Notice again that the domain field refers to your one-per-tenant System API.

Both handlers rely upon the Auth0 library you imported earlier. If you apply this and refresh the app, you can click the log-in button and be redirected to the Auth0 log-in page. This page is the “universal log-in” portal (Auth0 also supports integrating a “lock box” component). Notice that it automatically supports both username/password and social log-ins.

Show and hide content based on auth

Listing 6 has a few more script changes for index.html to implement show/hide functionality.

LEAVE A REPLY