class
JWT::JWKS
- JWT::JWKS
- Reference
- Object
Overview
JWKS (JSON Web Key Set) helper for JWT validation with support for OIDC discovery
Supports:
- Fetching OIDC metadata from /.well-known/openid-configuration
- Fetching and caching JWKS keys
- Local JWT validation (for service-to-service tokens)
- Remote JWT validation via JWKS
Example:
# Initialize with optional local keys
jwks = JWT::JWKS.new(
local_keys: {"local_key_id" => "secret"},
local_algorithm: JWT::Algorithm::HS256
)
# Validate a token and check scopes
payload = jwks.validate(token, issuer: "https://example.com", audience: "my-app")
if payload
scopes = JWT::JWKS.extract_scopes(payload)
if scopes.includes?("read")
# User has required scope
end
end
Defined in:
jwt/jwks.crjwt/jwks/jwk.cr
jwt/jwks/oidc_metadata.cr
Constant Summary
-
ALLOWED_ALGORITHMS =
{"RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512", "EdDSA"}
-
Allowed algorithms for JWKS validation (interoperable with real-world JWKS endpoints) "none" is explicitly excluded for security
-
DEFAULT_CACHE_TTL =
10.minutes
-
Default cache TTL (10 minutes)
-
DEFAULT_LEEWAY =
60.seconds
-
Default leeway for time-based claims (60 seconds)
Constructors
-
.new(local_keys : Hash(String, String) | Nil = nil, local_algorithm : Algorithm | Nil = nil, cache_ttl : Time::Span = DEFAULT_CACHE_TTL, leeway : Time::Span = DEFAULT_LEEWAY)
Initialize JWKS validator
Class Method Summary
-
.extract_roles(payload : JSON::Any) : Array(String)
Extract roles from JWT payload
-
.extract_scopes(payload : JSON::Any) : Array(String)
Extract scopes from JWT payload
-
.validate_roles(payload : JSON::Any, required_roles : Array(String)) : Bool
Validate roles in a JWT payload
-
.validate_scopes(payload : JSON::Any, required_scopes : Array(String)) : Bool
Validate scopes in a JWT payload
Instance Method Summary
-
#cache_ttl : Time::Span
Cache TTL
-
#clear_cache : Nil
Clear all caches
-
#fetch_jwks(jwks_uri : String, force_refresh : Bool = false) : JWKSet
Fetch JWKS from a jwks_uri
-
#fetch_oidc_metadata(issuer : String) : OIDCMetadata
Fetch OIDC metadata for an issuer
-
#find_key(jwks : JWKSet, kid : String) : JWK | Nil
Find a JWK by kid in a JWKS
-
#leeway : Time::Span
Clock skew leeway for time-based claims (exp, nbf, iat)
-
#leeway=(leeway : Time::Span)
Clock skew leeway for time-based claims (exp, nbf, iat)
- #local_algorithm : Algorithm | Nil
-
#local_keys : Hash(String, String) | Nil
Local keys for service-to-service JWT validation
-
#validate(token : String, issuer : String | Nil = nil, audience : String | Array(String) | Nil = nil, validate_claims : Bool = true) : JSON::Any | Nil
Validate a JWT token
Constructor Detail
Initialize JWKS validator
@param local_keys Optional hash of kid => key for local JWT validation @param local_algorithm Algorithm to use for local keys @param cache_ttl Cache TTL for OIDC metadata and JWKS (default: 10 minutes) @param leeway Clock skew leeway for time-based claims (default: 60 seconds)
Class Method Detail
Extract roles from JWT payload
Checks "roles" (Azure AD), "realm_access.roles" (Keycloak realm roles), "resource_access" (Keycloak client roles), and "groups" (Okta) claims
Extract scopes from JWT payload
Checks "scp" (Entra/Azure AD), "scope" (standard), and "permissions" (Auth0) claims Handles both space-delimited strings and arrays
Validate roles in a JWT payload
@param payload JWT payload @param required_roles Required roles (checks for "roles" claim) @return true if all required roles are present
Validate scopes in a JWT payload
@param payload JWT payload @param required_scopes Required scopes (checks for "scp" claim) @return true if all required scopes are present
Instance Method Detail
Fetch JWKS from a jwks_uri
@param jwks_uri JWKS URI @param force_refresh Force refresh even if cached (used for key rotation) @return JWKS key set
Fetch OIDC metadata for an issuer
@param issuer Issuer URL (e.g., "https://login.microsoftonline.com/{tenant}/v2.0") @return OIDC metadata
Validate a JWT token
This method will:
- Try to validate using local keys if provided
- Fall back to JWKS validation if not a local token
@param token JWT token string @param issuer Expected issuer (for OIDC metadata lookup) @param audience Expected audience(s) for validation @param validate_claims Whether to validate standard claims (exp, nbf, etc.) @return Validated payload or nil if validation fails
Example:
payload = jwks.validate(token, issuer: "https://example.com", audience: "my-app")
if payload
# Check scopes
scopes = JWT::JWKS.extract_scopes(payload)
if scopes.includes?("read")
# Token is valid with required scope
end
end