All Posts
JWTAPI TestingAuthentication

How to Generate Mock JWT Tokens for API Testing: RS256, Claims, and JWKS

Kiran MayeeApril 15, 20258 min read

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.

Share this article:

About the Author

Kiran Mayee

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.

Ready to build?

Start deploying serverless functions in under a minute.

Get Started Free