JWT Structure & Validation

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

AuthOS release 0.8.5 API v1 Latest-only documentation
Updated Jul 15, 2026
On this page

AuthOS uses JSON Web Tokens (JWT) with server-side session and authorization state. This guide describes the current token structure and validation steps; secure operation still depends on issuer/audience validation, key management, rotation, revocation, transport, and application configuration.

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",
  "is_platform_owner": false,
  "jti": "unique-token-id",
  "iat": 1704067200,
  "exp": 1704068100,
  "iss": "https://sso.example.com",
  "aud": "service:acme-corp/billing-app",
  "scope": null
}

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.
  • scope: OAuth scope string for resource-scoped access tokens.

Server-Side Hydration: Claims such as plan, features, and permissions are not included in the JWT. The AuthOS API resolves them from an internal cache (moka) using the sub (User ID). No fixed cache latency or immediate revocation-propagation guarantee is made.

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.