Deno - Create, Decode, and Verify JWT Token Examples

This tutorial shows you how to create, decode, and verify JWT tokens in Deno.

JSON Web Token (JWT) is an Internet standard for creating data. The tokens are signed with a secret key. Therefore, it can be used to prove whether the sender is legitimate only by checking the token.

The structure of a JWT token consists of header, payload, and signature. The header usually contains two keys: alg and type which identifies the algorithm to generate the signature and the token type (JWT) respectively. Example:

  {
    "alg": "HS512",
    "typ": "JWT"
  }

The payload contains several claims. You can pass any field in the payload. There are several reserved claims which are not required. However, if you include any of the reserved claims, you should follow the convention what is the field used for. Those reserved claims are:

  • iss (issuer): The issuer of the JWT.
  • sub (subject): The subject of the JWT (the user).
  • aud (audience): Recipient for which the JWT is intended.
  • exp (expiration time): The time after which the JWT expires.
  • nbf (not before time): The time before which the JWT must not be processed.
  • iat (issued at time): The time at which the JWT was issued.
  • jti (JWT ID): A unique identifier that can be used to prevent the JWT for being used multiple times.

Below is an example of a payload that has all the reserved claims above plus an additional field.

  {
    "iss": "woolha.com",
    "sub": "41e9546b-72ae-42d6-874d-040664b0e01d",
    "aud": "service1.woolha.com",
    "exp": 1609408207,
    "iat": 1609406407,
    "nbf": 1609406407,
    "name": "tuwaise",
    "jit": "813733181947831983434"
  }

The last part is signature which is calculated by concatenating base64URL encoding of the header and base64URL encoding of the payload separated with a . (period). It uses the algorithm defined on the header.

Using djwt Module

To use the remote module, import and re-export the functions to be used in deps.ts file. For this tutorial, we are going to use the following functions.

  • getNumericDate: Returns seconds since January 1, 1970, 00:00:00 UTC.
  • create: Used to create the JWT token.
  • decode: Used to decode JWT token.
  • verify: Used to verify the signature.

deps.ts

  import {
    getNumericDate,
    create as jwtCreate,
    decode as jwtDecode,
    verify as jwtVerify,
  } from 'https://deno.land/x/djwt@v2.0/mod.ts';

  export { getNumericDate, jwtCreate, jwtDecode, jwtVerify };

Create JWT Token

Below is the function for creating a JWT token.

  function create(
    header: Header,
    payload: Payload,
    key: string,
  ): Promise<string>

To use the function, you are required to pass three arguments. The first argument's type is Header. Header itself has the following fields.

  • alg: Algorithm: The signing algorithm to use.
  • [key: string]: unknown:

For the first argument, you need to pass an object. The object must have alg field whose value is the signing algorithm to use. It allows you to pass fields other than alg. Besides alg, usually the header also contains a type field which indicates the type of the token, which is JWT.

As of version 2.0, the module supports the following algorithms.

  • HS256 (HMAC SHA-256)
  • HS512 (HMAC SHA-512)
  • RS256 (RSASSA-PKCS1-v1_5 SHA-256)

The second argument's type is Payload which has the following fields.

  • iss?: string: The issuer of the JWT.
  • sub?: string: The subject of the JWT (the user).
  • aud?: string[] | string: Recipient for which the JWT is intended.
  • exp?: number: The time after which the JWT expires.
  • nbf?: number: The time before which the JWT must not be processed.
  • iat?: number: The time at which the JWT was issued.
  • jti?: string: A unique identifier that can be used to prevent the JWT for being used multiple times.
  • [key: string]: unknown: Custom field.

All the fields above are optional. You can also pass fields other than listed above. That allows you to add any data depending on your needs. However, for the fields listed above which are reserved JWT claims, the passed values must comply with the defined type and purpose for each field if you choose to include any of those fields as payload.

The last argument is the secret key used to create the signature.

Below is an example of how to create a JWT token using the module.

  import {
    getNumericDate,
    jwtCreate,
  } from './deps.ts';

  const payload = {
    iss: 'woolha.com',
    sub: '41e9546b-72ae-42d6-874d-040664b0e01d',
    aud: 'service1.woolha.com',
    exp: getNumericDate(30 * 60),
    iat: getNumericDate(new Date()),
    nbf: getNumericDate(new Date()),
    name: 'tuwaise',
    jit: '813733181947831983434',
  }
  const jwt = await jwtCreate({ alg: 'HS512', typ: 'JWT' }, payload, 'your-secret');

Output:

  jwt: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ3b29saGEuY29tIiwic3ViIjoiNDFlOTU0NmItNzJhZS00MmQ2LTg3NGQtMDQwNjY0YjBlMDFkIiwiYXVkIjoic2VydmljZTEud29vbGhhLmNvbSIsImV4cCI6MTYwOTQwNzg2OSwiaWF0IjoxNjA5NDA2MDY5LCJuYmYiOjE2MDk0MDYwNjksIm5hbWUiOiJ0dXdhaXNlIiwiaml0IjoiODEzNzMzMTgxOTQ3ODMxOTgzNDM0In0.ctIZXeuweyyQz-Mi6hBClTJhNStyanNLpGXSl8KfohyWeOHe-zAMgR-VfsPn5TIhep-GZEfG7CsNqmXxPQ8p7g
 

Decode JWT Token

The decode function can be used to decode a JWT token. It returns the header, payload, and signature of the token. Unless you encrypt the token, it can be decoded without the need of any secret key. Therefore, do not include any sensitive information if the token is not encrypted.

  function decode(
    jwt: string,
  ): {
    header: Header;
    payload: Payload;
    signature: string;
  }

Example:

  import { jwtDecode } from './deps.ts';

  const decoded = jwtDecode(jwt);
  console.log(`decoded: ${JSON.stringify(decoded)}`);

Output:

  decoded: {
    "header": {
      "alg": "HS512",
      "typ": "JWT"
    },
    "payload": {
      "iss": "woolha.com",
      "sub": "41e9546b-72ae-42d6-874d-040664b0e01d",
      "aud": "service1.woolha.com",
      "exp": 1609407869,
      "iat": 1609406069,
      "nbf": 1609406069,
      "name": "tuwaise",
      "jit": "813733181947831983434"
    },
    "signature": "72d2195debb07b2c90cfe322ea1042953261352b726a734ba465d297c29fa21c9678e1defb300c811f957ec3e7e532217a9f866447c6ec2b0daa65f13d0f29ee"
  }
 

Verify JWT Token

Below is the function to verify the validity of the signature.

  async function verify(
    jwt: string,
    key: string,
    algorithm: AlgorithmInput,
  ): Promise<Payload>

To use the function, you need to pass the jwt token as the first argument, the key used for creating signature as the second argument, and the signing algorithm as the third argument. If the signature is valid, it will return the payload.

Example:

  import { jwtVerify } from './deps.ts';

  const payload = await jwtVerify(jwt, 'your-secret', 'HS512');
  console.log(`payload: ${JSON.stringify(payload, null, 2)}`);

Output:

  payload: {
    "iss": "woolha.com",
    "sub": "41e9546b-72ae-42d6-874d-040664b0e01d",
    "aud": "service1.woolha.com",
    "exp": 1609407869,
    "iat": 1609406069,
    "nbf": 1609406069,
    "name": "tuwaise",
    "jit": "813733181947831983434"
  }

If the passed signature or the secret is invalid, you will get the following error.

  error: Uncaught Error: The jwt's signature does not match the verification signature.

Summary

That's how to create JWT tokens in Deno along with how to decode and verify the tokens. The djwt module can be used if your application needs to use JWT as authentication.