Skip to content
Search

JSON Web Token

lastmod 2026-09-10created 2019-01-2023 edits

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

Structure

  • HEADER (ALGORITHM & TOKEN TYPE)
  • PAYLOAD (DATA)
  • VERIFY SIGNATURE

Each of the parts above (all strings) is Base64-encoded and then joined with . as follows.

HEADER.PAYLOAD.SIGNATURE

Information (payload)

The Payload part contains the information to be carried in the token. Each 'piece' of information stored here is called a claim, and it consists of a name / value pair. A token can contain multiple claims.

Claims are broadly divided into three categories:

  • registered claims,
  • public claims,
  • private claims

Registered claims

Registered claims are claims with predefined names, meant to carry information about the token rather than information needed by the service. Use of registered claims is entirely optional, and the claim names included are:

  • iss: token issuer
  • sub: token subject
  • aud: token audience
  • exp: token expiration time (expiraton); the time must be in NumericDate format (e.g. 1480849147370) and must always be set later than the current time.
  • nbf: Means Not Before, a concept similar to the token's activation date. The date is also given in NumericDate format, and the token is not processed until this date has passed.
  • iat: the time the token was issued (issued at); this value can be used to determine the token's age.
  • jti: A unique identifier for the JWT, mainly used to prevent duplicate processing. Useful for one-time tokens.

Public claims

Public claims must have collision-resistant names. To avoid collisions, claim names are written in URI format.

{
    "https://velopert.com/jwt_claims/is_admin": true
}

Private claims

These are neither registered claims nor public claims. They are claim names used by agreement between the two parties (usually client <-> server). Unlike public claims, names may be duplicated and collide, so be careful when using them.

{
    "username": "velopert"
}

Example Payload

{
    "iss": "velopert.com",
    "exp": "1485270000000",
    "https://velopert.com/jwt_claims/is_admin": true,
    "userId": "11028373727102",
    "username": "velopert"
}

Where to store JWT

Two-line summary:

  • Use a Refresh token.
  • cookie or localStorage, if you must pick one? Use localStorage.

Renewing JWT Refresh tokens with Axios

JWT Security

We use JSON Web Tokens (JWTs) quite a lot in the OAuth and OpenID Connect world.

  1. JWTs used as Access Tokens
  2. What algorithms to use
  3. When to validate the token
  4. Always check the issuer
  5. Always check the audience
  6. Make sure tokens are used as intended
  7. Dealing with expiration, issued time and clock skew
  8. How to work with the signature
  9. When to use symmetric signing
  10. Pairwise Pseudonymous Identifiers
  11. Do not use JWTs for sessions

Example

decodeJWTPayload with decodeBase64URL

export function decodeBase64URL(value: string): string {
  const key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
  let base64 = ''
  let chr1, chr2, chr3
  let enc1, enc2, enc3, enc4
  let i = 0
  value = value.replace('-', '+').replace('_', '/')

  while (i < value.length) {
    enc1 = key.indexOf(value.charAt(i++))
    enc2 = key.indexOf(value.charAt(i++))
    enc3 = key.indexOf(value.charAt(i++))
    enc4 = key.indexOf(value.charAt(i++))
    chr1 = (enc1 << 2) | (enc2 >> 4)
    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2)
    chr3 = ((enc3 & 3) << 6) | enc4
    base64 = base64 + String.fromCharCode(chr1)

    if (enc3 != 64 && chr2 != 0) {
      base64 = base64 + String.fromCharCode(chr2)
    }
    if (enc4 != 64 && chr3 != 0) {
      base64 = base64 + String.fromCharCode(chr3)
    }
  }
  return base64
}

// Taken from: https://stackoverflow.com/questions/38552003/how-to-decode-jwt-token-in-javascript-without-using-a-library
export function decodeJWTPayload(token: string) {
  // Regex checks for base64url format
  const base64UrlRegex = /^([a-z0-9_-]{4})*($|[a-z0-9_-]{3}=?$|[a-z0-9_-]{2}(==)?$)$/i

  const parts = token.split('.')

  if (parts.length !== 3) {
    throw new Error('JWT is not valid: not a JWT structure')
  }

  if (!base64UrlRegex.test(parts[1])) {
    throw new Error('JWT is not valid: payload is not in base64url format')
  }

  const base64Url = parts[1]
  return JSON.parse(decodeBase64URL(base64Url))
}

reservationTokenRenewal

reservationTokenRenewal(access: string, refresh: string) {
    const encodedPayload = access.split('.')[1];
    const payloadJsonText = atob(encodedPayload)
    const parsed = JSON.parse(payloadJsonText.toString());
    const hasExp = parsed.hasOwnProperty("exp");
    const hasIat = parsed.hasOwnProperty("iat");

    if (!hasExp || !hasIat) {
        if (!hasExp) {
            console.error('Missing `exp` in JWT payload');
        }
        if (!hasIat) {
            console.error('Missing `iat` in JWT payload');
        }
        return;
    }

    // const nowUtc = moment();
    // const expUtc = moment(parsed.exp * 1000);
    // const iatUtc = moment(parsed.iat * 1000);
    // console.debug('Access token expiration: ' + expUtc.toISOString());

    const expMilliseconds = (parsed.exp * 1000 - LEEWAY_MILLISECONDS);
    const timeout = expMilliseconds - Date.now();
    if (timeout < 0) {
        console.warn('After a while, the token will expire.');
        return;
    }

    if (typeof this.refreshTimeoutId !== 'undefined') {
        window.clearTimeout(this.refreshTimeoutId);
        this.refreshTimeoutId = undefined;
    }
    this.refreshTimeoutId = window.setTimeout(() => {
        this.refreshToken(refresh);
    }, timeout);

    const renewalTime = moment(Date.now() + timeout).toISOString();
    console.debug(`Token renewal timeout: ${timeout}ms (${renewalTime})`);
}

Documentation

  • rfc 7519

Libraries

See also

Favorite site

Online tools

References


  1. Where_should_the_JWT_be_stored_-_localStorage_vs_cookie.pdf ↩

  2. Maruzzings_devlog_-_axios_interceptors_token_refresh.pdf ↩

  3. Secure_login_handling_at_the_frontend.pdf ↩

author Zer0 sitemap