Implementing JWT Authentication

This tutorial guides you through the process of creating a simple authentication server that generates a JSON Web Token. You’ll see how the client can use this JWT to authenticate with Ably. You will also learn how the client can automatically request a new JWT, before the current JWT expires.

Ably supports two types of authentication schemes:

When using token authentication, Ably recommends you instantiate the client library with a method to obtain tokens, rather than a token itself. This is due to the fact that tokens eventually expire, resulting in the connection being lost. By providing a method to obtain tokens instead, the Ably client library will automatically get a new token once the current one expires, maintaining a continuous connection.

JSON Web Tokens (JWTs) are an open, industry standard (RFC 7519) method for representing claims securely between two parties. JWT auth is especially helpful in scenarios where a single authentication scheme is to be used for all devices and browsers, and third-party platforms such as Ably. JWT auth is also a convenient way to authenticate your IoT devices or servers which are built using the platforms that Ably doesn’t yet support. This is possible because you don’t have to instantiate an Ably client when using the JWT auth scheme.

Ably offers two different ways in which you can use a JWT to authenticate your users:

  • You can use the JWT as the token itself by formatting it correctly (called an Ably JWT).
  • You can pass to Ably a JWT of any structure that contains an Ably Token.

In this tutorial, the first of these options will be used.

In the following steps you will:

  • Build a simple login form that will authenticate a client with Ably, using a JWT.
  • Learn how to use the jsonwebtoken npm library, which is an implementation of JWT. This library allows your auth server to conveniently create a JWT by specifying only certain service specific data in the payload and headers.
  • Create a web app that provides an endpoint that returns a JWT that can be used to autheticate your Ably client.

So, let’s get started and see a typical client-server architecture using JWT authentication.

Ably JWT auth example – Live Demo

This section contains a live demo of the project you will create in this tutorial.

Enter any username and password into the following dialog and click the Login button:

If the server returns a valid JWT the client is authenticated with Ably, and you see an alert box with the message: “Client successfully connected to Ably using JWT auth”.

The full code for this project can be found in its Ably GitHub repository.

Running the project locally

In this section you’ll learn how to clone the project code and run it locally. Alternatively, you can work through creating the project step-by-step.

The project can be run locally using the following steps:

1. Make sure that you have a free Ably account.

2. Make sure you have Node.js and npm installed on your system.

3. Clone the project:

git clone https://github.com/ably/jwt-auth-node-tutorial

4. Change into the project directory jwt-auth-node-tutorial.

5. Copy env.example to .env.

6. Edit .env and replace the placeholder text with your Ably API key.

7. Enter npm install on the command line to install the required packages.

8. Enter npm start on the command line to start the server.

9. Navigate your browser to http://localhost:3000.

You will see a dialog similar to the following displayed:


JWT Demo

9. Enter any username and password and click Login.

An alert box is displayed containing the message “Client successfully connected to Ably using JWT auth”.

Step 1 – Create your Ably app and API key

To follow this tutorial, you will need an Ably account. Sign up for a free account if you don’t already have one.

Access to the Ably global messaging platform requires an API key for authentication. API keys exist within the context of an Ably application and each application can have multiple API keys so that you can assign different capabilities and manage access to channels and queues.

You can either create a new application for this tutorial, or use an existing one.

To create a new application and generate an API key:

  1. Log in to your Ably account dashboard
  2. Click the “Create New App” button
  3. Give it a name and click “Create app”
  4. Copy your private API key and store it somewhere. You will need it for this tutorial.

To use an existing application and API key:

  1. Select an application from “Your apps” in the dashboard
  2. In the API keys tab, choose an API key to use for this tutorial. The default “Root” API key has full access to capabilities and channels.
  3. Copy the Root API key and store it somewhere. You will need it for this tutorial.

    Copy API Key screenshot

Step 2 – Creating the server

This step shows you how to create the server code from scratch. If you get stuck at any point, you can always refer to the complete code.

1. Make sure that you have Node.js and npm installed on your system.

2. Install the jsonwebtoken npm library, and also the Express web framework:

npm install jsonwebtoken express

3. Copy your API key to the clipboard, from your Ably Dashboard. It will be of the form 12AB3a.ab3GOm:c-B4Emryq9aFmOxP.

4. Create a .env file with the following content:

API_KEY=your_api_key

5. Paste in your API key to replace your_api_key.

6. Create a new file called server.js. Add the following code at the top of the file:

const jwt = require("jsonwebtoken") 
require('dotenv').config()

This includes the JWT library you’ll be using, and also loads the environment file containing your API_KEY.

7. Add the following code:

const apiKey = process.env.API_KEY
const keyParts = apiKey.split(':', 2);
const keyName   = keyParts[0];
const keySecret = keyParts[1];
const ttlSeconds = 3600

The Ably API key is split into two parts here: keyName, which is used for the keyId in the JWT, and keySecret, which is used to sign the JWT.

The ttlSeconds constant is the duration (time-to-live) in seconds for which the JWT will be valid for. After this time the token has expired and can no longer be used for authentication, and a new one must be created.

A JWT is typically made up of three different entities of data: a header, a payload and a signature. However, since you are using the jsonwebtoken npm library, these details are abstracted away and you only need a key secret and any application-specific payload data. You can also add other options, such as if you need to use a non-default hashing algorithm.

8. With Ably, you must send the time-to-live as well as the keyid as part of the jwtOptions parameter. You can optionally configure capabilities for the client using this token or a client ID, these will be part of the payload. Add the following code:

const jwtPayload = {                                                                                                                                                                                                                                           
    'x-ably-capability': JSON.stringify({ '*': ['publish', 'subscribe'] }),                                                                                                                                                                                    
    'x-ably-clientId': 'OPTIONAL_CLIENT_ID'                                                                                                                                                                                                                    
}                                                                                                                                                                                                                                                              

const jwtOptions = {                                                                                                                                                                                                                                           
    expiresIn: ttlSeconds,                                                                                                                                                                                                                                     
    keyid: `${keyName}`                                                                                                                                                                                                                                         
}      

9. To finish the web app you use Express to build a simple server to serve files on your localhost:

const express = require('express'),
    app = express();
app.use('/', express.static(__dirname))

The Express server uses a middleware to serve the content, and uses express.static(__dirname) to specify that any HTML files in the root folder are served directly.

10. Create the /auth route in this server. This route is responsible for creating a JWT and sending this token back to the client:

app.get('/auth', (req, res) => {
    console.log('Sucessfully connected to the server auth endpoint')
    jwt.sign(jwtPayload, keySecret, jwtOptions, (err, tokenId) => {
        console.log('JSON Web Token signed by auth server')
        if (err) {
            console.trace()
            return
        }
        res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
        res.setHeader('Content-Type', 'application/json')
        console.log('Sending signed JWT token back to client')
        res.send(JSON.stringify(tokenId));
    })

})

The previous code is invoked when a client application invokes the /auth route on your auth server.

You have used the jsonwebtoken npm library to sign a JWT using the Ably API key which you obtained from your dashboard. The callback function returns an error parameter and the tokenId. You then check for the error if any, otherwise send the JWT back to the client in order to confirm its authentication with Ably.

Note that a cache-control has been added to the response header in order to prevent a cached token from being obtained. This makes sure you never obtain a token which has potentially expired.

11. Set the web server to listen on a suitable port – 3000 is used in the following code example:

app.listen(3000, function () {
    console.log('Web server listening on port 3000');
});

Complete code for this tutorial can be found in its GitHub repository. The server code you have just created can be found in server.js.

Step 3 – Creating the client

You will now learn how to create a client with a simple login form. For the simplicity of this tutorial, your code will authenticate all users, irrespective of the data they enter in the username and the password fields. However, in a real deployment, your auth server would verify this data according to profile information stored in your user database and generate and return a JWT back to the client only if the verification was successful.

1. A simple styling template is used to improve the design of the login form. Paste this CSS code into css/style.css.

2. Now you will create the HTML file, index.html, for your front-end client. Embed the JavaScript logic directly into this file for simplicity. Add the following HTML to your index.html file:

<html>
<head>
  <script src="https://cdn.ably.io/lib/ably.min-1.js" type="text/javascript"></script>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <section class="container">
    <div class="login">
      <h1>Ably JWT auth example</h1>
      <form>
        <input type="text" placeholder="Enter Username" name="username">
        <br/>
        <input type="password" placeholder="Password" name="password">
        <br/>
        <p class="submit">
          <button type="submit" onclick="login(event)">Login</button>
        </p>
      </form>
    </div>
  </section>
</body>
</html>

As you can see, it’s a simple HTML skeleton with a form containing two input fields, one for each username and password as well as a button for logging in. Ably is included through the CDN and the code also includes the stylesheet that you previously created. When the button is clicked, it invokes the login function that you’ll now add using JavaScript.

The last part is to add the logic into this form. You’ll instantiate Ably’s Realtime client library and request a JWT. Please note that for simplicity, the code ignores the username and password credentials, which, in a practical deployment, would be verified by your auth server before signing a JWT and sending it back to the client.

3. Add the following JavaScript code within the head tag of your HTML file, right below the link to your CSS file:

<script type="text/javascript">
console.log('On login page now')
function login(e) {
    e.preventDefault();
    console.log('Fetching JWT token from auth server')
    const realtime = new Ably.Realtime({ authUrl: '/auth' });
    realtime.connection.once('connected', function () {
    console.log('Client connected to Ably using JWT')
    alert("Client successfully connected to Ably using JWT auth")
    })
}
</script>

The code begins by instantiating Ably’s Realtime client library, then you assign the path of auth server, /auth to the authUrl parameter. The /auth path of on auth server is responsible for signing a JWT using the private API key and then returning back the tokenId, the JWT itself.

With the returned JWT, the client automatically attempts to connect to Ably using it. If the client successfully authenticates with Ably, the code displays an alert to let the user know that the connection was successful.

Complete code for this tutorial can be found in its GitHub repository.

Step 4 – Checking the output

Now that you have all of your files in place, go ahead and test it out.

1. Start your command line/terminal.

2. Change into your project directory.

3. Start your server by running:

node server.js

4. Navigate your browser to http://localhost:3000, since your Express server is listening on port 3000.

You will see the login form. Open the browser JavaScript console if you want to see the logged messages.

5. Enter any data into the username and password fields and click login.

You will see an alert with the message “Client successfully connected to Ably using JWT auth”.

Next steps

1. Find out how Basic Auth and Token Auth differ and how to choose the right authentication scheme
2. Read up on Basic Auth and how to use Basic Auth on your server using the REST library
3. Read up on Token Auth and how to use Token Auth on a client using the Realtime library
4. Understand and see some examples of how to define capabilities for tokens and token requests
5. Discover how identities in tokens allow clients and servers to trust other clients’ identities validated and provided by your servers
6. Learn more about other Ably features by stepping through our other Ably tutorials
7. Gain a good technical overview of how the Ably realtime platform works
8. Obtain complete code for this tutorial from its GitHub repository.
9. Get in touch if you need help