Grant Video Access Tokens with Your Express + TypeScript Video API

April 08, 2021
Written by
Mia Adjei
Twilion
Reviewed by

Grant Video Access Tokens with Your Express + TypeScript Video API

So you've built a video API with Twilio Programmable Video, Node.js, TypeScript, and Express. You've added CORS support so that your future video app will be able to access resources on your server. What's next?

In order for users to actually be able to connect to video rooms in your app, they will need to be authenticated using an access token. In this tutorial, you'll update your Express + TypeScript video API to add a new route that grants access tokens to the users who will be using your video app. Let's get started!

Prerequisites

You will need:

  • A free Twilio account. (If you register here, you'll receive $10 in Twilio credit when you upgrade to a paid account!)
  • Node.js (version 14.16.1 or higher) and npm installed on your machine.
  • HTTPie or cURL.
  • The code from the previous tutorial (see below).

The rest of this tutorial builds on the code you wrote in the previous tutorial for adding CORS support to your video API. If you don't already have that code, you can find it in this GitHub repository on the added-cors branch. Follow the instructions in the repository's README.md file to get up and running.

Once you've run the command npm run start, you should see a log in your terminal telling you that your server is running on port 5000:

Express server listening on port 5000

You’re ready to begin!

Create an auth router

You already have a router in your code for handling video-room-related requests. It’s time to create a new router that will handle your authorization requests. In the case of your video API, this will be the part of the API that generates a signed access token and returns it to the client side so that your users can join a video room.

While this specific tutorial does not expand on user management, you may want to consider adding user management logic to your API as well. Each user should have a unique identifier (userId), such as a username, email address, or UUID.

You may want to add a user router that handles creating, listing, updating, and deleting users, as well as connect your favorite database to store your user data. Additionally, consider adding a login route that will make sure that only registered and verified users can retrieve tokens for your video app.

Navigate to the src directory and create a new file called auth.ts. This is where your auth router will live.

Open this file in your code editor and add the following imports at the top of the file:

import { Router } from 'express';
import config from '../config';
import twilio from 'twilio';

Then, create a new router to handle authorization requests:

const authRouter = Router();

Next, add the constants below. These constants represent the access token you will create for the user, the video grant that will be attached to that token, and the maximum amount of time a participant's session in a video room can last:

const AccessToken = twilio.jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;

// Maximum number of seconds that a participant's video room session can last (4 hours)
const MAXIMUM_SESSION_DURATION = 14400;

Now, create the route that will generate your access token:

/**
 * Get a token for a user for a video room
 */
authRouter.post('/token', async (request, response, next) => {
  // Implementation forthcoming!
});

Inside your new route, write some code to get the user's unique identifier (userId) and the video room's unique identifier (roomSid) from the incoming API request:

  // Get the userId and roomName from the request.
  const userId: string  = request.body.userId;
  const roomSid: string = request.body.roomSid;

Make sure to handle the case where any environment variables may be missing. They shouldn't be missing, but it's good to handle situations like this just in case. If an environment variable is missing, return an error response early, letting the client side know that the server was not able to generate an access token:

  // Handle case where environment variables could be missing.
  if (!config.TWILIO_ACCOUNT_SID || !config.TWILIO_API_KEY || !config.TWILIO_API_SECRET) {
    return response.status(400).send({
      message: 'Unable to create access token'
    });
  }

Next, add the following code to create a new AccessToken, associate it with the user, and grant access to the specific video room whose roomSid was passed into the API request:

  // Create an access token.
  const token = new AccessToken(
    config.TWILIO_ACCOUNT_SID,
    config.TWILIO_API_KEY,
    config.TWILIO_API_SECRET,
    { ttl: MAXIMUM_SESSION_DURATION }
  );

  // Associate this token with the user from the request.
  token.identity = userId;

  // Grant the access token Twilio Video capabilities.
  const grant = new VideoGrant({ room: roomSid });
  token.addGrant(grant);

Return the access token to the client in the JSON response:

  // Serialize the token to a JWT and include it in the JSON response.
  return response.status(200).send({
    userId: userId,
    roomSid: roomSid,
    token: token.toJwt(),
  });

The completed route should look like the code below:

/**
 * Get a token for a user for a video room
 */
authRouter.post('/token', async (request, response, next) => {

  // Get the userId and roomName from the request
  const userId: string  = request.body.userId;
  const roomSid: string = request.body.roomSid;

  // Handle case where environment variables could be missing.
  if (!config.TWILIO_ACCOUNT_SID || !config.TWILIO_API_KEY || !config.TWILIO_API_SECRET) {
    return response.status(400).send({
      message: 'Unable to create access token'
    });
  }

  // Create an access token.
  const token = new AccessToken(
    config.TWILIO_ACCOUNT_SID,
    config.TWILIO_API_KEY,
    config.TWILIO_API_SECRET,
    { ttl: MAXIMUM_SESSION_DURATION }
  );

  // Associate this token with the user from the request.
  token.identity = userId;

  // Grant the access token Twilio Video capabilities.
  const grant = new VideoGrant({ room: roomSid });
  token.addGrant(grant);

  // Serialize the token to a JWT and include it in the JSON response.
  return response.status(200).send({
    userId: userId,
    roomSid: roomSid,
    token: token.toJwt(),
  });
});

Finally, export your authRouter so it can be used by other parts of your server:

  export default authRouter;

Your auth router is complete!

Import and test your auth router

Open src/index.ts in your code editor. Import your authRouter by adding the following line just below the other imports at the top of the file:

import authRouter from './routes/auth';

Then, just above the roomsRouter line, add the following code that will let your server use this authRouter for all requests to the /auth URI:

// Forward requests for the /auth URI to our auth router
app.use('/auth', authRouter);

Alright! You've got all the code you need. It's time to test out retrieving an access token!

For testing purposes, try to generate an access token for a user with the username Beyoncé who wants to join a video room in your app that has a roomSid of RM12345678901234567890123456789012.

If you are using HTTPie, run the following command in your terminal:

http POST localhost:5000/auth/token userId="Beyoncé" roomSid="RM12345678901234567890123456789012"

If using cURL, run this command instead:

curl -X POST localhost:5000/auth/token \
    -d '{"roomSid":"RM12345678901234567890123456789012", "userId":"Beyoncé"}' \
    -H "Content-Type: application/json"

In the response, you will see that an access token was created and returned to you!

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 470
Content-Type: application/json; charset=utf-8
Date: Tue, 30 Mar 2021 21:32:34 GMT
ETag: <ETag>
Keep-Alive: timeout=5
Vary: Origin
X-Powered-By: Express

{
    "roomSid": "RM12345678901234567890123456789012",
    "token": <ACCESS_TOKEN_HERE>,
    "userId": "Beyoncé"
}

Awesome!

If you would like to see the code for this tutorial in its entirety, feel free to check it out on the access-token-added branch of this GitHub repository.

What to build next with your Express + TypeScript video API and Twilio Programmable Video

Your video API is now able to provide access tokens for your users. You're all set to connect this API to the client-side app of your choice. What kind of exciting app will you be adding video to? A chat app perhaps? A screen-sharing app? Or perhaps you want to try your hand at using Twilio Functions to generate your access tokens instead? The sky's the limit! ⭐️

I can't wait to see what you build!

Mia Adjei is a Software Developer on the Developer Voices team. They love to help developers build out new project ideas and discover aha moments. Mia can be reached at madjei [at] twilio.com.