Well, I have built my web application with NodeJS and DynamoDB as backend. Now I need to build a login system to fulfill user access.

So, all I need to do is building a couple of APIs to update my DBs with username and password, also query by username and match password while logging in …

Wait. It seems so intuitive. What if credential of DB leaks and user information got dumped? Or, what if user wants to reset password? Reset links could be cracked easily if it’s built as simple as a GET request with random generated string - which is pseudo random of course!

Luckily, I found JWT and JWKS. I also found Auth0 - a well-known authentication provider - who provides support for JWKS, to achieve my implementation.

TL;DR

  • If you are developing a web application, lucky for you. Auth0 provided proficient SDKs for various of languages. Authentication and athorization could be done within 50 lines of codes!
  • If you don’t like default GUI provided by SDK. Lock might be what you are looking for. It provided all kinds of features to customize your style.
  • If you prefer APIs than SDK, refer to Auth0 APIs. Basically it exposes its authentication interfaces to users, so you can play around with it more like a programmer …
  • No luck, if you are building a PC or even embeded program like what I’m going to do, you need to know how JWT work with JWKS, and what role Auth0 is playing between them.

Get started

As a muggle in security field, I feel struggling when I need to build such a system from scratch. Out of security concern, I denied myself when I attempted to create a new table in DB to store password and handle session with Passport library, then used random string to reset it. Then, I find Auth0 as a solution.

Auth0 is an authentication library as well as a provider. It follows OAuth 2.0 protocol flow.

OAuth_abstract_flow

Here, Auth0 acts as Authorization Server, who issues tokens for trusted users. We will come to tokens later.

Setup Auth0

Register

You need to register to access Auth0 services. There is a certain amount of free tier so don’t worry at early stage!

Go here for all you want!

Log into console

Sometimes GUI makes management much easier. Log in your Auth0 account and you will see your dashboard.

BTW, the login interface is what you will see defaultly in your application as well. Pretty cool.

Here are a bunch of terms to learn in the page. Just for now, you need to know:

  • Tenant: management unit under your account
  • Client: authentication unit of certain application. (Under tenant)

What now?

There is a stack of items in nav bar on the left side.

For now,

  1. Click Client to create a new client for your web application.
  2. Click APIs to make a new API that will be used later.

Authenticate web app users

For web app, just SDK, now backend involvement. I was using React so I refered to this page. The example code are self-explained well.

Install

# installation with npm
npm install --save auth0-js

# installation with yarn
yarn add auth0-js

Call out Lock interface

import auth0 from 'auth0-js';

export default class Auth {
  auth0 = new auth0.WebAuth({
    domain: 'domain.auth0.com',
    clientID: 'dEhy59RrO5ZWVw-upSq-sNpXbmcYeQEr',
    redirectUri: 'http://localhost:3000/callback',
    audience: 'https://domain.auth0.com/userinfo',
    responseType: 'token id_token',
    scope: 'openid'
  });

  login() {
    this.auth0.authorize();
  }
}
import Auth from './Auth/Auth.js';

const auth = new Auth();
auth.login();

As simple as it. We call the default Lock widget.

lock_widget

To customize this widget, refer to Lock

Add authentication

Now you have the interface. You might wonder how it works behind. The answer is, Auth0 returns three items when user login (with or without widget):

  • access_token: JWT token. Encoded with access information. Expiration time is also encoded inside it. It’s like the key for the door which invalidates itself over time.
  • id_token: User info comes to successful authentication.
  • expires_in: Decoded expiration time. It’s used to tell browser expiration information rather than invalidate “the key”.

More details

While authentication succeeds, call handleAuthentication():

this.auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        this.setSession(authResult);
        history.replace('/home');
      }

it parsed hash in url into tokens and store tokens into session.

It’s asynchronized. So you might want to add callback as handleAuthentication(callback).

Session is stored in localStorage while logout and removed from there while login:

...
localStorage.setItem('access_token', authResult.accessToken);
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('expires_at', expiresAt);
...

Authentication with APIs

If you prefer APIs, refer this. It work pretty like SDK, while you have more power, and more code to write of course!

Utilize Auth0 as Authentication service

If you are developing a destop app, or worse, an embedded app, there is no SDK or API built for you. You have to make them by hand. So it’s necessary to learn how it works behind all of it.

Make tokens work

Tokens, as mentioned before, act as different roles. The most significant one is access_token.

access_token_diagram

access_token is granted by auth_service, which is Auth0 in the context. After granted with access_token, client can access resources whenever it wants before expiration.

Of course it’s unwise to grant a long-life AT considering security. The solution is refresh_token.

refresh_token_diagram

RT never expire. Everytime AT get expired, RT is the “passport” to get a new “key”.

Both tokens are encoded by JWT and returned as JWKS issued by Auth0 service. Therefore, as long as your application can send Http request, it’s possible to login with tokens!

Get tokens

To debug Http requests and APIs, I recommand use Postman. It abstracts request codes, which makes things easier when working with APIs.

My task was composed of three part:

  1. get access_token & id_token
  2. get refresh_token for later use
  3. access protected data with access_token

Get access_token

Make request as follow:

req1

And get response like:

res1

You need to setup trusted identity provider in dashboard. Here I use Auth0 account itself. You can also use Facebook, Google, etc.

Get refresh_token

POST with following body:

req2

Now new access_token is in the response!

Access with AT

Build API for your authenticated access:

req3

Next section will introduce how to build backend API for this request.

Build API to serve authenticated users

The ideal API should block people away and let authenticated user in. As I was using NodeJS, All needed is a authentication middleware.

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

const authCheck = jwt({
    secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        // YOUR-AUTH0-DOMAIN name e.g prosper.auth0.com
        jwksUri: "https://domain.auth0.com/.well-known/jwks.json"
    }),
    // This is the identifier we set when we created the API
    audience: 'https://API',
    issuer: 'https://domain.auth0.com/',
    algorithms: ['RS256']
});

module.exports = authCheck;

This middleware is powered by JWT and JWKS, aiming to verify whether a user is authenticated to access the API.

Then, mount this middleware to API or router:

router.use(authCheck);

Now, authenticated user can access as normal, while others will be blocked out with 404.