JWT Lab API
JWT Lab is intentionally not a complete OAuth authorization server. It is a test token mint: submit claims, receive a correctly signed RS256 JWT, and validate it against the published JWKS.
Issue a token
POST /api/token
Content-Type: application/json
{
"preset": "delegated-agent",
"claims": {
"sub": "alice",
"aud": "https://bank.example/mcp",
"scope": "portfolio.read",
"act": { "sub": "investment-advisor-agent" }
},
"expiresIn": 3600
}
Preset claims are applied first; explicit claims override them, and a claim set to null drops the preset default. The service manages iss, iat, exp and jti unless advanced: true is supplied.
Preset categories
Core — user and workload (client_credentials-shaped) access tokens. MCP — tokens audience-bound to an MCP server per the MCP authorization spec, with sub for the principal and client_id for the OAuth client. Delegation — RFC 8693 token-exchange output shapes with act chains: authorize on sub plus the outermost actor only.
Token profiles — spec-shaped tokens with their mandated typ headers set automatically:
basic-user → typ: at+jwt (RFC 9068 required: iss, exp, aud, sub, client_id, iat, jti) spiffe-jwt-svid → SPIFFE JWT-SVID (sub is a SPIFFE ID; 5-minute lifetime; only sub, aud, exp required) transaction-token → typ: txntoken+jwt (required: txn, sub, aud=trust domain, scope, req_wl) id-jag → typ: oauth-id-jag+jwt (aud is the resource authorization server; client_id required)
Negative tests — validly signed tokens a conforming resource server must reject: expired and not-yet-valid (expect 401 invalid_token; introspection returns active: false). Other rejections (wrong iss, unbound aud, insufficient scope) are one claim edit away — set any claims you want in the payload. Edge cases — multi-valued aud arrays.
Discovery & validation
GET /.well-known/jwks.json GET /.well-known/oauth-authorization-server GET /.well-known/openid-configuration GET /api/presets
Introspect a token
POST /api/introspect
Content-Type: application/x-www-form-urlencoded
token=eyJhbGciOiJSUzI1NiIs...
{"active": true, "sub": "alice", "exp": 1767225600, ...}
RFC 7662 token introspection: JWT Lab verifies the signature (RS256 against its published JWKS) and exp/nbf itself and returns an OAuth introspection response. Invalid, expired, tampered or malformed tokens return {"active": false} with no claim echo. The endpoint is unauthenticated: tokens are public-key-signed JWTs anyone holding them can decode, so introspection adds only the issuer's live validity opinion. Because JWT Lab is stateless, it can never report a valid token as revoked.
MCP
Connect an MCP client to <your-lab-host>/mcp. The remote server exposes five tools:
issue_token introspect_token list_presets get_jwks get_oauth_metadata
The MCP interface uses the same issuance engine as the UI and REST API, so an agent can mint a token and immediately inject it into whatever integration it is testing.
Hosting setup
JWT Lab is host-agnostic: it runs on any Node environment (serverless, container or plain process). Configure the environment before serving traffic:
npm install npm run generate:key # Set the printed JWT_PRIVATE_KEY_B64 value in the hosting environment. # Also set: JWT_ISSUER=https://your-lab.example.com JWT_KID=jwt-lab-rs256-1
Important: keep the signing key stable. If an instance generates a different key, a token minted by one invocation might not validate against JWKS served by another.