nodejsnotes

Topic 024: Validating JSON Web Tokens (JWT) Using JSON Web Keys (JWKS) in Node.js Without jwks-rsa

Topic 024: Validating JSON Web Tokens (JWT) Using JSON Web Keys (JWKS) in Node.js Without jwks-rsa

Why Not Use jwks-rsa?

jwks-rsa simplifies the process but comes with limitations, especially regarding additional security configurations. Direct implementation offers more control over HTTP requests, error handling, and fine-tuning client secret authentication.

JWT Token Information

HEADER: Algorithm & Token Type

{
  "kid": "",
  "typ": "",
  "alg": "RS384"
}

PAYLOAD: Data

{
  "sub": "",
  "aud": "",
  "clientid": "",
  "at": "",
  "nbf": "",
  "DisplayName": "",
  "iss": "",
  "exp": "",
  "iat": "",
  "jti": "",
  "sid": ""
}

Verify Signature

Key Format Table

Key Type Purpose Common Formats
Public Key Used to verify the signature SPKI, PKCS #1, X.509 certificate, JWK format
Private Key Used to create the signature (stays secure) PKCS #8, PKCS #1, JWK format

Signature Creation and Verification Details

  1. Algorithm: RSASHA384
  2. Encoded String: Combine the header and payload in Base64 URL encoding, separated by a dot (.).
  3. Keys:
    • Public key for signature verification.
    • Private key for signing, securely stored and never leaves the browser.

Example Command (Dummy Format)

Below is a sample bash command with placeholder values.

# Step 1: Encode Header and Payload
header=$(echo -n '{"alg":"RS384","typ":"JWT"}' | base64 | tr '/+' '_-' | tr -d '=')
payload=$(echo -n '{"sub":"1234567890","name":"John Doe","iat":1516239022}' | base64 | tr '/+' '_-' | tr -d '=')
combined="$header.$payload"

# Step 2: Create Signature using Private Key
signature=$(echo -n "$combined" | openssl dgst -sha384 -sign private_key.pem | base64 | tr '/+' '_-' | tr -d '=')

# Step 3: Verify Signature using Public Key
echo -n "$combined.$signature" | openssl dgst -sha384 -verify public_key.pem -signature <(echo -n "$signature" | base64 -d)


---

### **JWKS Call - cURL Command**

```bash
curl --location --request GET 'URL' \
--header 'Accept: app/json' \
--header 'Content-type: app/json' \
--header 'client-id: abcde..z012..5' \
--header 'client-secret: 012..5abc...z'

JWKS Call - Response Example

{
  "keys": [
    {
      "kty": "",
      "e": "",
      "use": "",
      "kid": "",
      "alg": "",
      "n": "abc...(approx 256 characters)"
    },....
  ]
}

Node.js Code to Validate JWT Using JWKS

const axios = require("axios");
const jwt = require("jsonwebtoken");
const jwkToPem = require("jwk-to-pem");

// JWKS URL and client credentials
const jwksUrl = "https://example.com/.well-known/jwks.json";
const clientId = "your-client-id";
const clientSecret = "your-client-secret";

async function getJWKS() {
  try {
    const response = await axios.get(jwksUrl, {
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        "client-id": clientId,
        "client-secret": clientSecret,
      },
    });
    return response.data.keys;
  } catch (error) {
    throw new Error(`Failed to retrieve JWKS: ${error.message}`);
  }
}

function validateToken(token) {
  return getJWKS()
    .then((keys) => {
      const decodedHeader = jwt.decode(token, { complete: true }).header;
      const key = keys.find((k) => k.kid === decodedHeader.kid);

      if (!key) throw new Error("Matching key not found");

      const pem = jwkToPem(key);
      jwt.verify(token, pem, { algorithms: ["RS384"] }, (err, decoded) => {
        if (err) throw new Error("Token verification failed");
        console.log("Token is valid", decoded);
      });
    })
    .catch((error) => console.error(error.message));
}

// Sample token for validation
const token = "your-jwt-token";
validateToken(token);

Key Attributes and Their Significance

Making a JWKS Request Using Curl

A typical JWKS request can be executed using the following curl command, which includes the client ID and secret for authorization:

curl --location --request GET 'https://example.com/.well-known/jwks.json' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'client-id: your-client-id' \
--header 'client-secret: your-client-secret'

This command retrieves the JWKS data, including public keys used to validate the JWT signature.

References