JWT Structure & Validation

Comprehensive guide to AuthOS JSON Web Tokens, including structure, signing, and backend validation code examples.

Updated Apr 12, 2026 Edit this page

JWT Structure & Validation

AuthOS uses JSON Web Tokens (JWT) for stateless, secure authentication. This guide details the token structure, signing mechanism, and how to validate tokens in your backend services.

JWT Overview

A JWT consists of three parts separated by dots (.):

  1. Header: Algorithm and token type
  2. Payload: User data and claims
  3. Signature: Cryptographic proof of integrity
aaaaa.bbbbb.ccccc
Header.Payload.Signature

Signing Algorithm

All JWTs are signed using RS256 (RSA Signature with SHA-256).

  • Asymmetric: Uses a Private Key (held by AuthOS) to sign, and a Public Key (exposed via JWKS) to verify.
  • Security: You do not need any secrets to validate tokens.

Token Structure

1. Header

The header typically contains:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "sso-key-2025-01-01"
}
  • alg: Algorithm used (RS256)
  • typ: Token type (JWT)
  • kid: Key ID, essential for identifying which public key to use for validation.

2. Payload (Claims)

The payload contains statements about the user and authentication context (Claims).

{
  "sub": "user_123456789",
  "email": "user@example.com",
  "org": "acme-corp",
  "service": "billing-app",
  "plan": "enterprise",
  "features": ["audit-logs", "sso"],
  "iat": 1704067200,
  "exp": 1704068100,
  "iss": "https://sso.example.com",
  "aud": "billing-app"
}

Standard Claims

  • sub (Subject): Unique user identifier.
  • iss (Issuer): URL of the AuthOS instance.
  • aud (Audience): The service or client the token is intended for.
  • exp (Expiration Time): Unix timestamp when the token expires.
  • iat (Issued At): Unix timestamp when the token was created.

Custom Claims

  • org: Organization slug (if authenticated in org context).
  • service: Service slug (if authenticated for a specific service).
  • is_platform_owner: Boolean indicating super-admin status.
  • jti: Unique identifier for the JWT.

Server-Side Hydration: Claims such as plan, features, and permissions are NOT included in the JWT. Instead, the AuthOS API fetches these from a high-performance internal cache (moka) using the sub (User ID). This allows for instant permission revocation and smaller, more efficient tokens.

Token Validation

Your backend must validate every token before granting access.

JWKS Endpoint

Public keys are exposed at: GET /.well-known/jwks.json

Response:

{
  "keys": [
    {
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "kid": "sso-key-2025-01-01",
      "n": "base64-modulus...",
      "e": "AQAB"
    }
  ]
}

Validation Steps

  1. Extract Token: Get from Authorization: Bearer <token> header.
  2. Decode Header: Read the kid from the JWT header.
  3. Fetch Key: Look up the kid in the JWKS (cache this!).
  4. Verify Signature: Check that the signature matches the payload using the public key.
  5. Validate Claims: Ensure exp is in the future and iss/aud match expectations.

Code Examples

Node.js (Express)

Using express-jwt and jwks-rsa:

import { expressjwt } from 'express-jwt';
import jwksRsa from 'jwks-rsa';

const checkJwt = expressjwt({
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: 'https://sso.example.com/.well-known/jwks.json'
  }),
  audience: 'your-service-slug',
  issuer: 'https://sso.example.com',
  algorithms: ['RS256']
});

app.get('/api/private', checkJwt, (req, res) => {
  res.send(`Hello ${req.auth.email}!`);
});

Python (Flask)

Using pyjwt:

import jwt
from jwt import PyJWKClient

url = "https://sso.example.com/.well-known/jwks.json"
jwks_client = PyJWKClient(url)

def validate_token(token):
    signing_key = jwks_client.get_signing_key_from_jwt(token)
    data = jwt.decode(
        token,
        signing_key.key,
        algorithms=["RS256"],
        audience="your-service-slug",
        issuer="https://sso.example.com"
    )
    return data

Best Practices

  1. Cache JWKS: Don’t fetch keys on every request. Cache them.
  2. Rate Limit: Limit calls to the JWKS endpoint to prevent DOS.
  3. Check Scope/Context: Just because a token is valid doesn’t mean the user has access. Check org and features claims.
  4. Handle Rotation: Keys rotate periodically. Ensure your cache invalidates or refreshes when it encounters an unknown kid.