Skip to main content
The JWT Token Validator guardrail provides comprehensive JWT (JSON Web Token) validation and authentication for the Portkey Gateway. It supports signature verification, claim validation, and custom business logic rules.

Features

  • Signature Verification: Validates JWT signatures using JWKS (JSON Web Key Set)
  • Inline JWKS Support: Provide JWKS directly without requiring external URI
  • Token Introspection: Validates tokens via external introspection endpoint (RFC 7662)
  • Required Claims: Ensures specific claims are present in the token
  • Claim Value Validation: Validates claim values with flexible matching strategies
  • Header-Payload Matching: Ensures consistency between header and payload values
  • JWKS Caching: Efficient JWKS caching to reduce external calls (URI-based)
  • Introspection Caching: Cache introspection results with automatic expiry validation
  • Multi-tenant Support: Validate tenant IDs and other organizational claims
  • Role-Based Access: Validate user groups, roles, and permissions
  • OAuth2/OIDC Support: Standard audience, issuer, and scope validation
  • Claim Extraction: Extract JWT claims and inject them into request context as headers

Validation Methods

The guardrail supports three validation methods:
MethodBest ForProsCons
Inline JWKSTesting, static key deploymentsNo external dependencies, fastest validationRequires config updates for key rotation
JWKS URIProduction with key rotationAutomatic key rotation, caching, industry standardRequires network access
Token IntrospectionOpaque tokens, revocation supportWorks with opaque tokens, immediate revocationNetwork latency, external dependency

Configuration Parameters

Core Parameters

ParameterTypeRequiredDefaultDescription
jwksobjectConditional*-Inline JWKS object for token verification
jwksUristringConditional*-URI to fetch JSON Web Key Set for token verification
introspectEndpointstringConditional*-Token introspection endpoint URL (RFC 7662)
headerKeystringNo"Authorization"Header containing the JWT token
*Either jwks, jwksUri, or introspectEndpoint must be provided.

JWKS Validation Parameters

When using jwks or jwksUri:
ParameterTypeRequiredDefaultDescription
algorithmsstring[]No["RS256"]Allowed signing algorithms
cacheMaxAgenumberNo86400JWKS cache duration in seconds (24h) - only applies to jwksUri
clockTolerancenumberNo5Clock tolerance in seconds for time-based claims (exp, nbf)
maxTokenAgestringNo"1d"Maximum token age (e.g., ‘1d’, ‘12h’, ‘30m’)

Token Introspection Parameters

When using introspectEndpoint:
ParameterTypeRequiredDefaultDescription
introspectContentTypestringNo-Content type for Introspection Endpoint
introspectCacheMaxAgenumberNo-Cache introspection results for this many seconds
clockTolerancenumberNo5Clock tolerance in seconds for time-based claims

Claim Validation Parameters

ParameterTypeRequiredDescription
requiredClaimsstring[]NoArray of claim names that must be present
claimValuesobjectNoObject mapping claim names to expected values with match types
headerPayloadMatchstring[]NoArray of keys where header[key] must equal payload[key]
extractClaimsstring[]NoArray of claim names to extract and inject into request context
claimPrefixstringNoPrefix for extracted claim headers (default: "x-jwt-")

Match Types

The claimValues parameter supports different match types for flexible validation:

exact (default)

The claim value must exactly equal the expected value. Requires single-value string claim and single expected value. Use cases: iss, sub, client_id - single exact value validation
{
  "claimValues": {
    "iss": {
      "values": "https://auth.example.com"
    }
  }
}

contains

Array/string must contain at least one of the expected values (OR logic). Use this for array claims. Use cases: aud (JWT audience arrays), groups, roles, permissions
{
  "claimValues": {
    "groups": {
      "values": ["admin", "moderator"],
      "matchType": "contains"
    }
  }
}

containsAll

Array/string must contain ALL of the expected values (AND logic). Use this when ALL values are required. Use cases: scope (requires ALL specified scopes), required permissions
{
  "claimValues": {
    "scope": {
      "values": ["read:api", "write:api"],
      "matchType": "containsAll"
    }
  }
}

regex

Value must match the regex pattern. Use cases: Email domain validation, pattern matching
{
  "claimValues": {
    "email": {
      "values": ".*@(company1|company2)\\.com$",
      "matchType": "regex"
    }
  }
}

Quick Reference

Claim TypeExampleRecommended matchType
Single value, exact matchiss, sub, client_idexact
Single value, OR logictenant_id with multiple tenantscontains
Array - check if contains anyaud, groups (OR logic)contains
Array - check if contains allscope (AND logic)containsAll
Pattern matchingemail domain validationregex

Usage Examples

Basic JWT Validation (Inline JWKS)

{
  "jwks": {
    "keys": [
      {
        "kty": "RSA",
        "kid": "my-key-id",
        "use": "sig",
        "alg": "RS256",
        "n": "xGOr-H7A-PWH_4...",
        "e": "AQAB"
      }
    ]
  },
  "algorithms": ["RS256"]
}

Basic JWT Validation (JWKS URI)

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "algorithms": ["RS256"]
}

Token Introspection with Caching

{
  "introspectEndpoint": "https://auth.example.com/oauth/introspect",
  "introspectCacheMaxAge": 300,
  "clockTolerance": 5
}

Required Claims Validation

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "requiredClaims": ["sub", "email", "tenant_id"]
}

Issuer and Audience Validation

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "claimValues": {
    "iss": {
      "values": "https://auth.example.com"
    },
    "aud": {
      "values": ["https://api.example.com"],
      "matchType": "contains"
    }
  }
}

Group/Role Validation (OR logic)

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "claimValues": {
    "groups": {
      "values": ["admin", "moderator"],
      "matchType": "contains"
    }
  }
}

Scope Validation (AND logic)

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "claimValues": {
    "scope": {
      "values": ["read:api", "write:api"],
      "matchType": "containsAll"
    }
  }
}

Multi-Tenant Validation

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "requiredClaims": ["tenant_id"],
  "claimValues": {
    "tenant_id": {
      "values": ["tenant-123", "tenant-456", "tenant-789"],
      "matchType": "contains"
    }
  }
}

Email Domain Validation

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "claimValues": {
    "email": {
      "values": ".*@(company1|company2)\\.com$",
      "matchType": "regex"
    }
  }
}

Claim Extraction to Context

Extracts validated JWT claims and injects them as headers for downstream services:
{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "extractClaims": ["sub", "email", "tenant_id", "groups"],
  "claimPrefix": "x-jwt-"
}
Result: Claims are extracted and added as headers:
  • x-jwt-sub: user-123
  • x-jwt-email: [email protected]
  • x-jwt-tenant-id: tenant-456
  • x-jwt-groups: admin,developer

Comprehensive Production Setup

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "algorithms": ["RS256", "ES256"],
  "cacheMaxAge": 86400,
  "clockTolerance": 5,
  "maxTokenAge": "1d",
  "requiredClaims": ["sub", "email", "tenant_id"],
  "claimValues": {
    "iss": {
      "values": "https://auth.example.com"
    },
    "aud": {
      "values": "https://api.example.com",
      "matchType": "contains"
    },
    "tenant_id": {
      "values": ["tenant-123", "tenant-456"],
      "matchType": "contains"
    },
    "groups": {
      "values": ["admin", "developer"],
      "matchType": "contains"
    },
    "scope": {
      "values": ["read:api", "write:api"],
      "matchType": "containsAll"
    },
    "email": {
      "values": ".*@(company1|company2)\\.com$",
      "matchType": "regex"
    }
  },
  "headerPayloadMatch": ["kid"],
  "extractClaims": ["sub", "email", "tenant_id", "groups", "scope"],
  "claimPrefix": "x-jwt-"
}

Response Format

Success Response

{
  "error": null,
  "verdict": true,
  "data": {
    "verdict": true,
    "explanation": "JWT token validation succeeded",
    "validations": {
      "signatureValid": true,
      "requiredClaims": { "valid": true },
      "claimValues": { "valid": true },
      "headerPayloadMatch": { "valid": true }
    }
  },
  "transformedData": {
    "headers": {
      "x-jwt-sub": "user-123",
      "x-jwt-email": "[email protected]",
      "x-jwt-tenant-id": "tenant-456"
    }
  },
  "transformed": true
}

Failure Response

{
  "error": null,
  "verdict": false,
  "data": {
    "verdict": false,
    "explanation": "JWT validation failed: Missing required claims: email, tenant_id; Invalid claim values: groups",
    "validations": {
      "signatureValid": true,
      "requiredClaims": {
        "valid": false,
        "missing": ["email", "tenant_id"]
      },
      "claimValues": {
        "valid": false,
        "failed": ["groups"]
      }
    }
  }
}

Token Format

The guardrail expects JWT tokens in the following format:
Authorization: Bearer <JWT_TOKEN>
Or with a custom header:
X-API-Token: Bearer <JWT_TOKEN>
The Bearer prefix is optional and will be automatically stripped if present.

Common Patterns

API Gateway Authentication

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "requiredClaims": ["sub"],
  "claimValues": {
    "iss": { "values": "https://auth.example.com" },
    "aud": { "values": "https://api.example.com", "matchType": "contains" }
  }
}

Admin-Only Access

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "claimValues": {
    "groups": {
      "values": ["admin"],
      "matchType": "contains"
    }
  }
}

Tenant Isolation

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "requiredClaims": ["tenant_id"],
  "claimValues": {
    "tenant_id": {
      "values": ["<SPECIFIC_TENANT_ID>"]
    }
  }
}

Service-to-Service Authentication

{
  "jwksUri": "https://auth.example.com/.well-known/jwks.json",
  "requiredClaims": ["client_id"],
  "claimValues": {
    "scope": {
      "values": ["service:read", "service:write"],
      "matchType": "containsAll"
    }
  }
}

Best Practices

Security

  • Always validate the issuer (iss) to prevent token substitution attacks
  • Validate the audience (aud) to ensure tokens are intended for your API
  • Use appropriate clockTolerance (5-10 seconds) to handle clock skew
  • Set reasonable maxTokenAge to limit token lifetime
  • Enable headerPayloadMatch for kid to prevent key confusion attacks

Performance

  • Set appropriate cacheMaxAge (default 24h) to reduce JWKS fetches
  • Inline JWKS provides the fastest validation (no external calls)
  • Use introspection caching when using token introspection endpoint

Multi-Tenancy

  • Always include tenant_id in requiredClaims
  • Validate tenant_id values explicitly
  • Consider adding tenant-specific JWKS URIs for larger deployments

Claim Extraction

  • Only extract claims needed by downstream services
  • Use consistent claimPrefix across your infrastructure
  • Be mindful of header size limits when extracting large claims
  • Extracted claims are only added if validation succeeds

Troubleshooting

Token Not Found

Missing authorization header
Ensure the token is sent in the correct header (Authorization by default).

Invalid Signature

JWT signature validation error: ...
Verify your JWKS URI is correct and the token was signed by the expected issuer.

Missing Claims

JWT validation failed: Missing required claims: email, tenant_id
Ensure your auth provider includes these claims in the token.

Invalid Claim Values

JWT validation failed: Invalid claim values: groups
Check that the claim values in your token match the expected values in your configuration.

Clock Skew Issues

JWT signature validation error: token is expired
Increase clockTolerance to handle clock differences between systems.