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 read the specification of the Ably JWT token if you want to understand its construction.
  • 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 authenticate your Ably client.

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

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.

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 – Create 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 <app ID>.<key ID>:<key secret>.
For example: 12AB3a.ab3GOm:c-B4Emryq9aFmOxP.

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

ABLY_API_KEY=your_api_key

Paste in your API key to replace your_api_key.

5. 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 ABLY_API_KEY.

6. Add the following code:

const apiKey = process.env.ABLY_API_KEY;
const [ keyId, keySecret ] = apiKey.split(":");

const expiresIn = 3600;
const jwtOptions = { expiresIn, keyId };

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

The expiresIn constant is the number of seconds the JWT is valid for. After this time has elapsed, the token will expire and you must generate a new one to authenticate with.

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.

The Ably platform expects you to send the time-to-live (expiresIn) and 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.

7. In this step you will create a simple web server using the Express web framework to serve static files. In a later step you will also use this server to create an API route for clients to authenticate with.

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

app.use('/', express.static(__dirname))

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

8. In a production application you would check that the user exists and is authorized to use your service before issuing a JWT with their user ID as the clientId. In this example you will just use the user ID they provide.

In the index.html file, instantiate the Ably client library with a ClientOptions object, passing in your authentication endpoint URL as the authUrl property.

Append the user’s input to this URL as a query string in the format: /auth?clientId={username}:{password}. You can access the clientId parameter in the query string using the Express Request object, as req.query.clientId

This code is invoked when a client application makes a call to the /auth route on your server.

app.get("/auth", (req, res) => {
  console.log("Successfully connected to the server auth endpoint");

  const randomId = Math.random().toString(16).slice(-8);
  const clientId = req.query.clientId || randomId;

  const jwtPayload = {
    "x-ably-capability": capability,
    "x-ably-clientId": clientId,
  };

  jwt.sign(jwtPayload, keySecret, jwtOptions, (err, tokenId) => {
    console.log("JSON Web Token signed by auth server");

    if (err) return console.trace();

    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", tokenId);
    res.send(JSON.stringify(tokenId));
  });
});

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. If there is an error, you log it to the console. If the operation is successful, you send the JWT back to the client. This JWT contains a token that the client can use to authenticate with:

{
  "token": "xVLyHw.Dtxd9tuz....EXAMPLE",
  "capability": "{\"*\":[\"*\"]}",
  "clientId": "[email protected]",
  "expires": 1449745287315,
  "keyName": "xVLyHw.lCE5Tg",
  "issued": 1449741687315
}

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.

9. 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 – Create 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.com/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>
          <label for="username">Username</label>
          <input type="text" placeholder="Enter Username" id="username" />
          <label for="password">Password</label>
          <input type="password" placeholder="Password" id="password" />
          <button type="submit" onclick="login(event)">Login</button>
        </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();

  const username = document.querySelector("#username").value;
  const password = document.querySelector("#password").value;

  if (!`${username}`.trim() || !`${password}`.trim()) {
    return alert("Error! Require username and password");
  }

  // Send user details to the auth server
  const authUrl = `/auth?clientId=${username}:${password}`;
  const realtime = new Ably.Realtime({ authUrl });

  console.log("Requesting Ably JWT token from auth server", username, password);

  realtime.connection.once("connected", () => {
    const { tokenDetails } = realtime.auth;
    console.log("Client successfully connected to Ably using JWT auth", tokenDetails);
    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 the auth server, /auth to the authUrl parameter. The /auth path of the 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 – Check the output

Now you are ready to test your application.

1. Start your command line/terminal.

2. Change into your project directory.

3. Start your server by running:

node server.js

The server will output its log messages to your terminal.

4. Visit http://localhost:3000 in your browser (because your Express server is listening on port 3000).

In the browser you will see the login form.

5. Open the browser developer console, to see messages logged by the client.

6. Enter any values 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”.

Download tutorial source code

In this section you’ll learn how to download the tutorial solution code and run it locally.

Perform 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 Ably tutorials repository:

git clone [email protected]:ably/tutorials.git

4. Change to the project branch jwt-authentication-nodejs where the source code resides.

git checkout jwt-authentication-nodejs

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 Auth Demo

10. Enter any username and password and click Login.

An alert box is displayed containing 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
10. Read about third-party serverless integrations with Netlify Identity for Ably authentication