How to Generate Mock JWT Tokens for API Testing: RS256, Claims, and JWKS
Your backend validates JWT tokens on every request. The middleware checks the signature, verifies the expiry, extracts the user ID from claims. It works perfectly with your identity provider.
But now you need to test it. And you realise: generating a valid JWT that your backend will accept requires either a running auth server or a deep understanding of cryptographic signing.
The JWT Testing Problem
To create a JWT that passes validation, you need:
- A private key (RSA or ECDSA) to sign the token — or a shared secret for HS256.
- The correct
iss(issuer) claim matching what your backend expects. - The correct
aud(audience) claim. - An
exp(expiry) in the future. - A
sub(subject) claim with a valid user identifier. - Any custom claims your backend requires (roles, permissions, org_id).
- A JWKS endpoint serving the public key — because your backend fetches it to verify the signature.
Most developers end up doing one of these workarounds:
- Disable auth in tests — skip JWT validation entirely. Your tests pass, but they don't test the auth flow at all.
- Hardcode a token — create a token with a very long expiry and commit it. It works until the key rotates or the claims change.
- Run a local auth server — Keycloak, Hydra, or a custom Express app. Works but adds complexity and startup time.
- Mock the validation function — stub out the JWT middleware. Fast, but doesn't test the actual token parsing.
The Sandbox Solution: Real Tokens, Real Verification
moqapi.dev's Auth Sandbox generates real RS256-signed JWTs with a proper JWKS endpoint. Your backend validates them exactly as it would validate production tokens.
Getting a Token
# Client credentials — no user context
curl -X POST https://moqapi.dev/api/invoke/{projectId}/auth/token \
-d '{
"grant_type": "client_credentials",
"client_id": "moqapi_abc123",
"client_secret": "your-secret",
"scope": "openid profile email"
}'
# Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1vcWFwaS0xIn0...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile email"
}
Decoding the Token
The access token is a standard JWT with these claims:
// Header
{
"alg": "RS256",
"typ": "JWT",
"kid": "moqapi-1"
}
// Payload
{
"iss": "https://moqapi.dev/api/invoke/{projectId}/auth",
"sub": "client:moqapi_abc123",
"aud": "moqapi_abc123",
"exp": 1741443600,
"iat": 1741440000,
"scope": "openid profile email",
"client_id": "moqapi_abc123",
"token_type": "access_token"
}
Verifying the Signature
Your backend fetches the public key from the JWKS endpoint to verify the token:
# JWKS endpoint
curl https://moqapi.dev/api/invoke/{projectId}/auth/.well-known/jwks.json
# Returns:
{
"keys": [
{
"kty": "RSA",
"kid": "moqapi-1",
"use": "sig",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}
Point your backend's JWKS URL to the sandbox endpoint. It will fetch the public key and verify the signature — exactly as it does in production.
Configuring Your Backend for Sandbox Tokens
Most JWT libraries accept a JWKS URL and issuer. Switch them for testing:
// Node.js with jose library
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL(process.env.JWKS_URL || 'https://moqapi.dev/api/invoke/{projectId}/auth/.well-known/jwks.json')
);
async function verifyToken(token) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: process.env.JWT_ISSUER || 'https://moqapi.dev/api/invoke/{projectId}/auth',
audience: process.env.JWT_AUDIENCE || 'moqapi_abc123'
});
return payload;
}
Testing with User Context
Client credentials tokens don't have user claims. For tokens with user identity, use the password grant:
curl -X POST https://moqapi.dev/api/invoke/{projectId}/auth/token \
-d '{
"grant_type": "password",
"client_id": "moqapi_abc123",
"username": "testuser",
"password": "password123",
"scope": "openid profile email"
}'
# The token includes user claims:
{
"sub": "user-uuid",
"name": "testuser",
"email": "testuser@sandbox.moqapi.dev",
"roles": ["user"]
}
Token Introspection for Server-Side Validation
Some backends use token introspection (RFC 7662) instead of local JWT verification:
curl -X POST https://moqapi.dev/api/invoke/{projectId}/auth/introspect \
-d '{ "token": "YOUR_ACCESS_TOKEN" }'
# Response:
{
"active": true,
"sub": "user-uuid",
"client_id": "moqapi_abc123",
"scope": "openid profile email",
"token_type": "access_token",
"exp": 1741443600
}
Testing Token Expiry and Refresh
The sandbox configures token lifetimes via the config API. Set short expiries to test refresh logic:
// Set 10-second access token expiry for testing
POST /api/auth-sandbox/config
{
"projectId": "your-project",
"accessTokenTTL": 10,
"refreshTokenTTL": 300
}
// Get a token, wait 11 seconds, then:
// - Verify the access token → should be expired
// - Exchange the refresh token → should get new tokens
Key Takeaways
- Generating valid JWTs for testing requires signing keys, correct claims, and a JWKS endpoint.
- Common workarounds (disabling auth, hardcoding tokens) reduce test coverage and break over time.
- The Auth Sandbox generates real RS256 JWTs with a working JWKS endpoint — no auth server required.
- Point your backend's JWKS URL and issuer at the sandbox for seamless integration testing.
- Test token expiry, refresh, and introspection with configurable lifetimes.
Generate valid test tokens instantly at moqapi.dev/signup.
About the Author
Founder and sole developer of moqapi.dev. Full-stack engineer with deep experience in API platforms, serverless runtimes, and developer tooling. Built moqapi to solve the mock data and deployment friction she experienced firsthand building production APIs.
Related Articles
Automated Breaking Change Detection for REST APIs: A Practical Guide
Removed fields, renamed properties, changed enum values — breaking changes hide in plain sight. Learn how to detect them automatically with schema comparison, AI analysis, and severity classification.
Test OAuth 2.0 Flows Without an Identity Provider: The Auth Sandbox Approach
Setting up Auth0 or Okta just to test login flows is overkill. Learn how a local auth sandbox gives you working OAuth 2.0 endpoints, real JWTs, and configurable error injection — without a third-party IdP.
Testing API Authentication Error Handling: 401s, Expired Tokens, and Rate Limits
Your happy path works. But what happens when the token expires mid-session? When the refresh token is revoked? When the auth server rate-limits you? Here is how to test every auth failure mode systematically.
Ready to build?
Start deploying serverless functions in under a minute.