### Example OIDC Metadata URL Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md An example of a concrete OIDC metadata endpoint URL for Microsoft Azure AD. This URL returns a JSON object detailing the OIDC provider's configuration. ```text https://login.microsoftonline.com/common/.well-known/openid-configuration ``` -------------------------------- ### Custom Header Fields Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Demonstrates how to include custom header fields like 'typ' and other arbitrary fields when encoding a JWT. ```crystal JWT.encode(payload, key, alg, typ: "CustomType", custom_field: "value") ``` -------------------------------- ### Handle JWT::VerificationError Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Example demonstrating how to catch and handle a JWT::VerificationError when attempting to decode a token with an incorrect key. ```crystal token_signed_with_key_a = JWT.encode({}, "key_a", JWT::Algorithm::HS256) begin # Trying to verify with different key JWT.decode(token_signed_with_key_a, "key_b", JWT::Algorithm::HS256) rescue error : JWT::VerificationError puts "Signature verification failed: #{error.message}" end ``` -------------------------------- ### ID Token Signing Algorithms Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Accessing the array of supported signing algorithms for ID tokens. Common values include 'RS256' and 'ES256'. ```crystal metadata.id_token_signing_alg_values_supported # => ["RS256", "RS384", "RS512", "ES256"] ``` -------------------------------- ### Add JWT Dependency to Shard.yml Source: https://github.com/crystal-community/jwt/blob/master/README.md To install the JWT library, add the specified dependency to your application's shard.yml file. ```yaml dependencies: jwt: github: crystal-community/jwt ``` -------------------------------- ### Response Types Supported Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Accessing the array of supported response types for the authorization endpoint. Common values include 'code' and 'id_token'. ```crystal metadata.response_types_supported # => ["code", "id_token", "code id_token", "code token", "token", "id_token token"] ``` -------------------------------- ### Custom Cache TTL Configuration Source: https://github.com/crystal-community/jwt/blob/master/JWKS_README.md Set a custom time-to-live (TTL) for caching JWKS metadata and keys. This example caches for 5 minutes. ```crystal # Cache metadata and JWKS for 5 minutes jwks = JWT::JWKS.new(cache_ttl: 5.minutes) ``` -------------------------------- ### Encode JWT with Issued At (iat) Claim Source: https://github.com/crystal-community/jwt/blob/master/README.md Example of creating a JWT and including the 'iat' claim to indicate the time of issuance. ```crystal payload = { "foo" => "bar", "iat" => Time.utc.to_unix } token = JWT.encode(payload, "SecretKey", JWT::Algorithm::HS256) ``` -------------------------------- ### Userinfo Endpoint Property Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Accessing the userinfo endpoint URL from the OIDC metadata. This is used to retrieve authenticated user information. ```crystal metadata.userinfo_endpoint # => "https://graph.microsoft.com/oidc/userinfo" ``` -------------------------------- ### Subject Types Supported Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Retrieving the array of supported subject identifier types. Valid values are 'public' and 'pairwise'. ```crystal metadata.subject_types_supported # => ["public", "pairwise"] ``` -------------------------------- ### Example JWT Payload Structure Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Illustrates a typical JWT payload containing standard registered claims (like 'sub', 'exp', 'iss') and custom claims. ```json { "sub": "user123", "aud": ["app1", "app2"], "exp": 1703001600, "nbf": 1702915200, "iat": 1702911600, "iss": "https://auth.example.com", "jti": "unique-token-id", "custom_claim": "value" } ``` -------------------------------- ### Example JWT Payload with Standard Claims Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/types.md Demonstrates how to construct a JWT payload with common standard claims like subject, issuer, audience, expiration, not before, issued at, JWT ID, and type. Ensure 'exp' and 'nbf' are set as Unix timestamps. ```crystal payload = { "sub" => "user123", "iss" => "https://auth.example.com", "aud" => ["app1", "app2"], "exp" => Time.utc.to_unix + 3600, # Expires in 1 hour "nbf" => Time.utc.to_unix, "iat" => Time.utc.to_unix, "jti" => "unique-token-id", "typ" => "JWT" } ``` -------------------------------- ### Get X.509 Certificate SHA-1 Thumbprint (x5t) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Retrieve the X.509 certificate SHA-1 thumbprint, base64url-encoded. This is used to match keys by certificate thumbprint and is optional. ```crystal jwk.x5t # => "Vet89ISP_8kJHhCZFHCSgGi3n0w" ``` -------------------------------- ### Force JWKS Refresh for Key Rotation Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md This example demonstrates how to force a refresh of the JWKS when key rotation is suspected. It first attempts validation with the cached JWKS and, if that fails, proceeds to fetch fresh JWKS. ```crystal # Attempt validation with existing cache if payload = jwks.validate(token) return payload end # If validation failed, force refresh in case of key rotation jwks_set = jwks.fetch_jwks(jwks_uri, force_refresh: true) # Then retry validation (the validate method does this automatically) ``` -------------------------------- ### Authorization Endpoint Property Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Accessing the authorization endpoint URL from the OIDC metadata. This is used for initiating OIDC authentication flows. ```crystal metadata.authorization_endpoint # => "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" ``` -------------------------------- ### Issuer Property Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Accessing the issuer URL from the OIDC metadata. This value is used for token validation. ```crystal metadata.issuer # => "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0" ``` -------------------------------- ### Google OIDC Provider Metadata Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Example structure of OIDC metadata typically provided by Google. This includes issuer, JWKS URI, and authorization endpoints. ```crystal issuer: "https://accounts.google.com" jwks_uri: "https://www.googleapis.com/oauth2/v3/certs" authorization_endpoint: "https://accounts.google.com/o/oauth2/v2/auth" token_endpoint: "https://oauth2.googleapis.com/token" userinfo_endpoint: "https://www.googleapis.com/oauth2/v1/userinfo" ``` -------------------------------- ### Handle JWT::ExpiredSignatureError Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Example showing how to catch a JWT::ExpiredSignatureError when decoding an expired token. ```crystal # Token that expired 1 hour ago payload = {"foo" => "bar", "exp" => Time.utc.to_unix - 3600} token = JWT.encode(payload, "key", JWT::Algorithm::HS256) begin JWT.decode(token, "key", JWT::Algorithm::HS256) rescue error : JWT::ExpiredSignatureError puts "Token expired: #{error.message}" end ``` -------------------------------- ### JWKS URI Property Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Retrieving the JWKS URI from the OIDC metadata. This URL is used to fetch public keys for token verification. ```crystal metadata.jwks_uri # => "https://login.microsoftonline.com/common/discovery/v2.0/keys" ``` -------------------------------- ### Get Key Type (kty) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Retrieve the type of the JWK. This property is required and must be one of 'RSA', 'EC', or 'OKP'. ```crystal jwk.kty # => "RSA" ``` -------------------------------- ### Get EC/OKP Public Key Material (x) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Retrieve the X coordinate for EC keys or the public key material for OKP (Ed25519) keys, base64url-encoded. This property is present for both EC and OKP keys. ```crystal jwk.x # => "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis" ``` -------------------------------- ### Token Endpoint Property Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Retrieving the token endpoint URL from the OIDC metadata. This endpoint is used for token exchange or refresh. ```crystal metadata.token_endpoint # => "https://login.microsoftonline.com/common/oauth2/v2.0/token" ``` -------------------------------- ### Get RSA Public Key Exponent (e) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Retrieve the RSA public key exponent, base64url-encoded. This property is only present for RSA keys. A common value is 'AQAB'. ```crystal jwk.e # => "AQAB" ``` -------------------------------- ### Handle JWT Validation Errors with Exception Handling Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md This example shows how to handle potential JWT validation errors using a begin-rescue block. It catches `JWT::DecodeError` exceptions, providing detailed error messages when validation fails due to issues like invalid signatures, missing keys, or malformed tokens. ```crystal begin if payload = jwks.validate(token, audience: "my-app") puts "Valid token for user: #{payload["sub"]}" else puts "Invalid token - signature or key not found" end rescue error : JWT::DecodeError puts "Validation error: #{error.message}" end ``` -------------------------------- ### JWT.decode Block Form Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Demonstrates the usage of the block form for JWT.decode, where the block dynamically determines the signing key based on the 'kid' (key ID) from the token's header. This is useful for managing multiple signing keys. ```crystal payload, header = JWT.decode(token, algorithm: JWT::Algorithm::RS256) do |header, payload| kid = header["kid"].as_s # Extract key ID from header # Look up the appropriate key if kid == "current_key" @current_key elsif kid == "previous_key" @previous_key else raise JWT::DecodeError.new("Unknown key ID") end end ``` -------------------------------- ### JWT.decode Return Value Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Illustrates the structure of the return value from JWT.decode, which is a tuple containing the decoded payload and header. The payload is a JSON::Any object, and the header is a Hash. ```crystal payload, header = JWT.decode(token, key, algorithm) # payload is JSON::Any representing the decoded claims payload = { "sub" => "user123", "exp" => 1234567890, "custom_claim" => "value" } # header is Hash(String, JSON::Any) with algorithm and type info header = { "typ" => "JWT", "alg" => "HS256" } ``` -------------------------------- ### End Session Endpoint Property Example Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Retrieving the end session endpoint URL from the OIDC metadata. This URL is used for terminating user sessions. ```crystal metadata.end_session_endpoint # => "https://login.microsoftonline.com/common/oauth2/v2.0/logout" ``` -------------------------------- ### Auth0 OIDC Provider Metadata Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Example structure of OIDC metadata typically provided by Auth0. This includes issuer, JWKS URI, and authorization endpoints. ```crystal issuer: "https://example.auth0.com/" jwks_uri: "https://example.auth0.com/.well-known/jwks.json" authorization_endpoint: "https://example.auth0.com/authorize" token_endpoint: "https://example.auth0.com/oauth/token" userinfo_endpoint: "https://example.auth0.com/userinfo" ``` -------------------------------- ### Get X.509 Certificate SHA-256 Thumbprint (x5t#S256) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the X.509 certificate SHA-256 thumbprint, base64url-encoded. This is more secure than the SHA-1 thumbprint and is optional. The JSON key is 'x5t#S256'. ```crystal jwk.x5t_s256 # => "wnqmuPvz2BjFZWRnSK72Q3jGpBm0dB..." ``` -------------------------------- ### Get Permitted Key Operations (key_ops) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Retrieve the array of permitted operations for the JWK, such as ['verify', 'sign']. JWKS validation requires 'verify' in key_ops when the key is used for signature verification. This property is optional. ```crystal jwk.key_ops # => ["verify"] ``` -------------------------------- ### Get Key Algorithm (alg) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Retrieve the intended algorithm for the JWK, like 'RS256' or 'EdDSA'. This property is optional. If present, it must match the token's 'alg' header. ```crystal jwk.alg # => "RS256" ``` -------------------------------- ### Get Key Use (use) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the intended use of the JWK, such as 'sig' for signing or 'enc' for encryption. This property is optional. JWKS validation requires use='sig' for signature verification. ```crystal jwk.use # => "sig" ``` -------------------------------- ### Validate JWT Token with Specific Issuer and Audience Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md This example demonstrates validating a JWT token with explicit issuer and audience parameters. It's useful when the OIDC provider's issuer URL is known or when the token can be accepted by multiple audiences. The `token` variable must be defined. ```crystal payload = jwks.validate( token, issuer: "https://login.microsoftonline.com/{tenant}/v2.0", audience: ["api://my-app", "api://my-app/scopes"] ) if payload puts "User: #{payload["unique_name"]}" else puts "Invalid token" end ``` -------------------------------- ### Microsoft Azure AD OIDC Provider Metadata Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Example structure of OIDC metadata typically provided by Microsoft Azure AD. This includes issuer, JWKS URI, and authorization endpoints. ```crystal issuer: "https://login.microsoftonline.com/{tenantId}/v2.0" jwks_uri: "https://login.microsoftonline.com/common/discovery/v2.0/keys" authorization_endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" token_endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token" end_session_endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/logout" ``` -------------------------------- ### Complete JWKS Validation Flow Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Provides a comprehensive example of validating a JWT by fetching JWKS, finding the appropriate key, and decoding the token. This flow is essential for verifying the authenticity and integrity of JWTs issued by external providers. ```crystal # Create JWKS validator jwks = JWT::JWKS.new # Fetch metadata for issuer issuer = "https://login.microsoftonline.com/common/v2.0" metadata = jwks.fetch_oidc_metadata(issuer) puts "Issuer: #{metadata.issuer}" puts "JWKS URI: #{metadata.jwks_uri}" # Fetch JWKS jwk_set = jwks.fetch_jwks(metadata.jwks_uri) puts "Available keys: #{jwk_set.keys.map(&.kid).join(", ")}" # Find key by ID from token header token_header = # ... extract from token kid = token_header["kid"].as_s jwk = jwk_set.keys.find { |k| k.kid == kid } if jwk # Convert to PEM and verify token pem = jwk.to_pem begin payload, header = JWT.decode( token, key: pem, algorithm: JWT::Algorithm::RS256, verify: true, validate: true, aud: "my-app" ) puts "Token valid for user: #{payload["sub"]}" rescue error : JWT::VerificationError puts "Invalid signature" rescue error : JWT::ExpiredSignatureError puts "Token expired" end else puts "Key not found in JWKS" end ``` -------------------------------- ### Import JWT Module and Core Functions Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/README.md Import the main JWT library and demonstrate basic usage of encode and decode functions. Also shows how to use JWKS for token validation and handle JWT-specific errors like expired signatures. ```crystal require "jwt" # Main module JWT::encode(...) JWT::decode(...) # JWKS support jwks = JWT::JWKS.new(...) jwks.validate(token) # Error handling begin JWT.decode(token, key, algorithm) rescue error : JWT::ExpiredSignatureError # Handle expired token end ``` -------------------------------- ### Handle JWT::ImmatureSignatureError Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Example of catching a JWT::ImmatureSignatureError when attempting to decode a token that is not yet valid. ```crystal # Token that becomes valid in 1 hour payload = {"foo" => "bar", "nbf" => Time.utc.to_unix + 3600} token = JWT.encode(payload, "key", JWT::Algorithm::HS256) begin JWT.decode(token, "key", JWT::Algorithm::HS256) rescue error : JWT::ImmatureSignatureError puts "Token not yet valid: #{error.message}" end ``` -------------------------------- ### Fetch and Iterate JWKS Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Demonstrates how to fetch JWKS metadata and JWKS from a URI, then iterate through the keys to find specific ones or list available key IDs and algorithms. Use this to dynamically retrieve and inspect keys from an OIDC provider. ```crystal jwks = JWT::JWKS.new metadata = jwks.fetch_oidc_metadata("https://example.com") jwk_set = jwks.fetch_jwks(metadata.jwks_uri) # Find a specific key current_key = jwk_set.keys.find { |k| k.kid == "current" } # List all key IDs key_ids = jwk_set.keys.map(&.kid) puts "Available keys: #{key_ids.join(", ")}" # Get algorithms in the set algorithms = jwk_set.keys.map(&.alg).uniq puts "Algorithms: #{algorithms}" ``` -------------------------------- ### Run Tests Source: https://github.com/crystal-community/jwt/blob/master/README.md Execute the test suite for the crystal-jwt library. ```shell crystal spec ``` -------------------------------- ### leeway Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md Gets and sets the clock skew leeway applied to time-based claims. This property can be modified after initialization. ```APIDOC ## leeway ### Description Getter and setter for the clock skew leeway applied to time-based claims. Can be modified after initialization. ### Property `leeway : Time::Span` ``` -------------------------------- ### Get EC Y Coordinate (y) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the Y coordinate for EC keys, base64url-encoded. This property is only present for EC keys. ```crystal jwk.y # => "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKcE" ``` -------------------------------- ### Convert JWK to PEM and Decode Token Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/types.md Demonstrates converting a JWK object to PEM format and then using the PEM key to decode a JWT token. This is useful for verifying token signatures. ```crystal jwk = # ... from JWKS pem = jwk.to_pem # => "-----BEGIN PUBLIC KEY-----\n..." payload, _ = JWT.decode(token, key: pem, algorithm: JWT::Algorithm::RS256) ``` -------------------------------- ### Encode JWT with Hash Payload Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Example of encoding a JWT using a standard Hash as the payload, including subject and expiration claims. ```crystal JWT.encode({"sub" => "user123", "exp" => Time.utc.to_unix + 3600}, key, JWT::Algorithm::HS256) ``` -------------------------------- ### Handle JWT::InvalidIssuerError Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Example showing how to catch a JWT::InvalidIssuerError when the provided issuer does not match the token's 'iss' claim. ```crystal payload = {"foo" => "bar", "iss" => "auth.example.com"} token = JWT.encode(payload, "key", JWT::Algorithm::HS256) begin # Requesting validation with non-matching issuer JWT.decode(token, "key", JWT::Algorithm::HS256, iss: "other.example.com") rescue error : JWT::InvalidIssuerError puts "Invalid issuer: #{error.message}" end ``` -------------------------------- ### Dynamic Key Selection during Decoding Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/INDEX.md This pattern allows for dynamic selection of the decoding key based on the 'kid' (key ID) header. It's useful when multiple keys might be used for signing. ```crystal payload, header = JWT.decode(token) do |header, payload| kid = header["kid"].as_s my_keys[kid] end ``` -------------------------------- ### Handle JWT::InvalidAudienceError Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Example demonstrating how to catch a JWT::InvalidAudienceError when the provided audience does not match the token's 'aud' claim. ```crystal payload = {"foo" => "bar", "aud" => ["service_a", "service_b"]} token = JWT.encode(payload, "key", JWT::Algorithm::HS256) begin # Requesting validation with non-matching audience JWT.decode(token, "key", JWT::Algorithm::HS256, aud: "service_c") rescue error : JWT::InvalidAudienceError puts "Invalid audience: #{error.message}" # Expected ["service_a", "service_b"], received nothing for "service_c" end ``` -------------------------------- ### Service-to-Service Tokens with Local Keys Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/INDEX.md This pattern is suitable for service-to-service communication where you manage your own keys locally. It uses a specified local algorithm for signing. ```crystal jwks = JWT::JWKS.new( local_keys: {"service-key" => "secret"}, local_algorithm: JWT::Algorithm::HS256 ) payload = jwks.validate(token) ``` -------------------------------- ### Encode and Decode with Asymmetric Keys (RSA) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/INDEX.md This pattern is for asymmetric encryption using RSA keys. The private key is used for encoding, and the public key is used for decoding. Keep the private key secure. ```crystal private_key = File.read("private.pem") public_key = File.read("public.pem") token = JWT.encode(payload, private_key, JWT::Algorithm::RS256) payload, header = JWT.decode(token, public_key, JWT::Algorithm::RS256) ``` -------------------------------- ### Convert JWT Algorithm to String and Parse from String Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/types.md Demonstrates how to convert a JWT Algorithm enum value to its string representation and how to parse an algorithm from a string. ```crystal algorithm = JWT::Algorithm::HS256 algorithm.to_s # => "HS256" # Parsing from string alg = JWT::Algorithm.parse("RS256") # => Algorithm::RS256 ``` -------------------------------- ### Get RSA Public Key Modulus (n) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the RSA public key modulus, base64url-encoded. This property is only present for RSA keys. ```crystal jwk.n # => "xGOr-H0A-6_BOXMq83k..." ``` -------------------------------- ### Encode and Decode with Not Before Time (nbf) Source: https://github.com/crystal-community/jwt/blob/master/README.md Shows how to create a JWT that is not valid until a specified future time. Attempts to decode before this time will raise an error. ```crystal nbf = Time.utc.to_unix + 60 payload = { "foo" => "bar", "nbf" => nbf } token = JWT.encode(payload, "SecretKey", JWT::Algorithm::HS256) # Currently it's not acceptable, raises JWT::ImmatureSignatureError JWT.decode(token, "SecretKey", JWT::Algorithm::HS256) ``` -------------------------------- ### Create Basic JWKS Validator Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md Instantiate a JWKS validator for OIDC token validation. No local keys or custom cache/leeway settings are configured. ```crystal jwks = JWT::JWKS.new ``` -------------------------------- ### Clock Skew Tolerance Configuration Source: https://github.com/crystal-community/jwt/blob/master/JWKS_README.md Configure a custom leeway in seconds for time-based claims (exp, nbf, iat) to tolerate clock skew between servers. Tokens within the specified leeway of expiry will be accepted. ```crystal # Configure custom leeway for time-based claims (exp, nbf, iat) jwks = JWT::JWKS.new(leeway: 120.seconds) # Tokens within 120 seconds of expiry will be accepted payload = jwks.validate(token, issuer: issuer, audience: audience) ``` -------------------------------- ### Get Key Identifier (kid) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the key identifier, which is used to match a JWK to a token's 'kid' header claim. This property is required. ```crystal jwk.kid # => "2024-key-001" ``` -------------------------------- ### Custom JWT Header with Additional Claims Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Demonstrates how to encode a JWT with custom header parameters like 'kid' and other arbitrary values. ```crystal JWT.encode(payload, key, JWT::Algorithm::HS256, kid: "key-123", custom: "value") # Results in header: # { # "typ": "JWT", # "alg": "HS256", # "kid": "key-123", # "custom": "value" # } ``` -------------------------------- ### Configure JWKS Cache and Leeway Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/INDEX.md Initialize a JWKS object with custom cache TTL and leeway for token validation. This helps manage performance and token expiration tolerance. ```crystal jwks = JWT::JWKS.new( cache_ttl: 30.minutes, leeway: 5.minutes ) ``` -------------------------------- ### Enforcing HTTPS for Metadata Fetching Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Demonstrates that the fetch_oidc_metadata method enforces HTTPS and will raise an error for HTTP URLs. ```crystal # This will raise JWT::DecodeError metadata = jwks.fetch_oidc_metadata("http://example.com") # ❌ HTTP not allowed ``` -------------------------------- ### Catching JWT Decode Errors Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Use this snippet to catch JWT::DecodeError, which occurs for malformed tokens, invalid Base64/JSON, or missing required headers/claims. The example assumes an invalid token string. ```crystal begin payload, header = JWT.decode("invalid.token", key, JWT::Algorithm::HS256) rescue error : JWT::DecodeError puts "Failed to decode: #{error.message}" end ``` -------------------------------- ### Fetch OIDC Metadata Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Fetches the OIDC metadata from a given issuer URL. This is the initial step to discover the provider's endpoints and keys. ```crystal jwks = JWT::JWKS.new metadata = jwks.fetch_oidc_metadata("https://login.microsoftonline.com/common/v2.0") ``` -------------------------------- ### Validating Token Issuer Against Metadata Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Shows how to ensure the 'iss' claim in a token matches the issuer found in the OIDC metadata. ```crystal expected_issuer = "https://login.microsoftonline.com/common/v2.0" metadata = jwks.fetch_oidc_metadata(expected_issuer) # Token's iss claim must match token_issuer = payload["iss"].as_s if token_issuer != metadata.issuer raise JWT::DecodeError.new("Invalid issuer") end ``` -------------------------------- ### Create JWKS Validator with Local Keys Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md Instantiate a JWKS validator with local keys for service-to-service token validation. Supports HMAC secrets, PEM keys, or hex strings. Requires specifying the local algorithm. ```crystal jwks = JWT::JWKS.new( local_keys: { "service-key-1" => "secret-key-value", "service-key-2" => File.read("service_key.pem") }, local_algorithm: JWT::Algorithm::HS256 ) ``` -------------------------------- ### Catching Unsupported Algorithm Errors Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md This snippet demonstrates how to catch JWT::UnsupportedAlgorithmError, which is raised when an unsupported algorithm is used or a JWK has an unsupported key type. Ensure the token and key are correctly defined. ```crystal begin # If token header has alg: "custom_alg" JWT.decode(token, key, algorithm: JWT::Algorithm::HS256) rescue error : JWT::UnsupportedAlgorithmError puts "Algorithm not supported: #{error.message}" end ``` -------------------------------- ### Get Cryptographic Curve (crv) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the cryptographic curve for EC or OKP keys. Valid values for EC include 'P-256', 'P-384', 'P-521', 'secp256k1'. For OKP, it's 'Ed25519'. This property is required for EC and OKP keys. ```crystal jwk.crv # => "P-256" ``` -------------------------------- ### Default Header Generation Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Shows the default 'typ' and 'alg' fields automatically included in the JWT header. ```json { "typ" => "JWT", # Default, can be overridden via typ parameter "alg" => algorithm.to_s # Based on algorithm parameter } ``` -------------------------------- ### Access Private Key Material (d) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwk.md Access the private key material. This property is optional and intended for private keys only. WARNING: This is security-sensitive and should never be transmitted or logged. ```crystal jwk.d # => "..." ``` -------------------------------- ### Bypassing JWT Verification Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/errors.md Decode JWT contents without performing signature verification or validation. Use this pattern when you only need to inspect the token's payload and header, for example, during debugging or for specific internal use cases where verification is handled elsewhere. ```crystal # Decode without verification to inspect token contents begin payload, header = JWT.decode(token, verify: false, validate: false) puts "Token claims: #{payload}" rescue error : JWT::DecodeError # Can still fail on malformed token structure puts "Token is malformed: #{error.message}" end ``` -------------------------------- ### Configure JWKS Leeway Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Instantiate JWT::JWKS with a custom leeway to allow for clock skew during token validation. ```crystal jwks = JWT::JWKS.new(leeway: 5.minutes) # Allow up to 5 minutes clock difference ``` -------------------------------- ### JWKS#fetch_oidc_metadata Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md Fetches and caches OIDC metadata from a provider's well-known endpoint. It validates the issuer URL, checks a cache for existing metadata, constructs the OIDC configuration URL, makes an HTTP GET request, and parses the JSON response. The metadata is then cached for a specified duration. ```APIDOC ## Method: JWKS#fetch_oidc_metadata ### Description Fetches and caches OIDC metadata from a provider's well-known endpoint. This method is essential for obtaining JWKS URIs and other configuration details needed for JWT validation. ### Signature ```crystal def fetch_oidc_metadata(issuer : String) : OIDCMetadata ``` ### Parameters #### Path Parameters - **issuer** (String) - Required - Issuer URL (e.g., "https://login.microsoftonline.com/{tenant}/v2.0"). Must use HTTPS. ### Return Type `JWT::JWKS::OIDCMetadata` - Metadata structure containing issuer, jwks_uri, and endpoint information. ### Behavior 1. Validates issuer uses HTTPS (raises DecodeError for HTTP). 2. Checks cache first (valid if not expired per cache_ttl). 3. Constructs well-known URL: `{issuer}/.well-known/openid-configuration`. 4. Makes HTTP GET request with 3-second connect timeout, 5-second read timeout. 5. Parses JSON response as OIDCMetadata. 6. Caches result for cache_ttl duration. 7. Returns metadata. ### Examples **Direct metadata fetch:** ```crystal jwks = JWT::JWKS.new metadata = jwks.fetch_oidc_metadata("https://login.microsoftonline.com/common/v2.0") puts "JWKS URI: #{metadata.jwks_uri}" puts "Token endpoint: #{metadata.token_endpoint}" ``` **Cached access:** ```crystal # First call: fetches from endpoint metadata1 = jwks.fetch_oidc_metadata(issuer) # Second call within 10 minutes: returns cached data (no HTTP request) metadata2 = jwks.fetch_oidc_metadata(issuer) ``` ### Throws - **JWT::DecodeError**: Issuer doesn't use HTTPS scheme. - **JWT::DecodeError**: HTTP request fails (non-2xx status code). - **JWT::DecodeError**: Response JSON is invalid. ``` -------------------------------- ### Encode and Decode with Expiration Time (exp) Source: https://github.com/crystal-community/jwt/blob/master/README.md Demonstrates creating a JWT with an expiration time and decoding it before and after expiration. Handles clock skew with a small leeway. ```crystal exp = Time.utc.to_unix + 60 payload = { "foo" => "bar", "exp" => exp } token = JWT.encode(payload, "SecretKey", JWT::Algorithm::HS256) # At this moment token can be decoded payload, header = JWT.decode(token, "SecretKey", JWT::Algorithm::HS256) sleep 61 # Now token is expired, so JWT::ExpiredSignatureError will be raised payload, header = JWT.decode(token, "SecretKey", JWT::Algorithm::HS256) ``` -------------------------------- ### Encode and Decode JWT with Issuer (iss) Claim Source: https://github.com/crystal-community/jwt/blob/master/README.md Illustrates creating a JWT with an issuer claim and successfully decoding it when the issuer matches. Shows that a mismatch raises an error. ```crystal payload = { "foo" => "bar", "iss" => "me"} token = JWT.encode(payload, "SecretKey", "HS256") # OK, because iss matches payload, header = JWT.decode(token, "SecretKey", JWT::Algorithm::HS256, iss: "me") # iss does not match, raises JWT::InvalidIssuerError payload, header = JWT.decode(token, "SecretKey", JWT::Algorithm::HS256, iss: "you") ``` -------------------------------- ### Directly Parse OIDC Metadata from JSON String Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Shows how to parse OIDC metadata directly from a JSON string using the `from_json` method. This is useful for testing or when the JSON is already available. ```crystal # Direct parsing json = %({"issuer":"https://example.com","jwks_uri":"https://example.com/keys"}) metadata = JWT::JWKS::OIDCMetadata.from_json(json) ``` -------------------------------- ### JWT::VERSION Constant Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-core.md Specifies the current version of the JWT library. ```crystal module JWT VERSION = "1.7.2" # Library version end ``` -------------------------------- ### Fetch OIDC Metadata Directly Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md Fetches OIDC metadata from the specified issuer URL and prints the JWKS URI and token endpoint. This is the primary way to obtain OIDC configuration details. ```crystal jwks = JWT::JWKS.new metadata = jwks.fetch_oidc_metadata("https://login.microsoftonline.com/common/v2.0") puts "JWKS URI: #{metadata.jwks_uri}" puts "Token endpoint: #{metadata.token_endpoint}" ``` -------------------------------- ### Default Cache TTL and Leeway Constants Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Defines the default time-to-live for cached OIDC metadata and JWKS responses, and the default clock skew tolerance. ```crystal DEFAULT_CACHE_TTL = 10.minutes DEFAULT_LEEWAY = 60.seconds ``` -------------------------------- ### Encode Token with Standard and Custom Claims Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/INDEX.md This snippet demonstrates how to construct a JWT payload including standard claims like expiration, issuer, audience, and subject, along with custom claims. ```crystal now = Time.utc.to_unix payload = { "sub" => "user123", "iss" => "my-app", "aud" => "api.example.com", "exp" => now + 3600, "nbf" => now, "iat" => now, "jti" => UUID.random.to_s, "custom_claim" => "value" } token = JWT.encode(payload, key, JWT::Algorithm::RS256) ``` -------------------------------- ### Encode JWT with Type Claim Source: https://github.com/crystal-community/jwt/blob/master/README.md Shows how to encode a JWT and explicitly set the 'typ' header parameter to 'JWT'. This is generally unnecessary as 'JWT' is the default value. ```crystal # NOTE typ defaults to "JWT" so setting it manually to this value is unnecessary payload = { "foo" => "bar" } token = JWT.encode(payload, "SecretKey", JWT::Algorithm::HS256, typ: "JWT") ``` -------------------------------- ### JWKS.new Constructor Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-jwks.md Creates a new JWKS validator instance. This constructor allows for configuring local keys for service-to-service validation, specifying the local algorithm, and setting cache TTL and leeway for time-based claims validation. ```APIDOC ## JWKS.new Constructor ### Description Creates a new JWKS validator instance with optional local key support and cache configuration. ### Signature ```crystal def initialize( @local_keys : Hash(String, String)? = nil, @local_algorithm : Algorithm? = nil, @cache_ttl : Time::Span = DEFAULT_CACHE_TTL, @leeway : Time::Span = DEFAULT_LEEWAY, ) ``` ### Parameters #### Path Parameters * **local_keys** (Hash(String, String)?) - Optional - Hash mapping key IDs to signing keys for internal/service-to-service token validation. Keys can be HMAC secrets, PEM keys, or hex strings. * **local_algorithm** (Algorithm?) - Optional - Algorithm for local key validation. Required if local_keys is provided. * **cache_ttl** (Time::Span) - Optional - Cache time-to-live for OIDC metadata and JWKS responses. Defaults to 10.minutes. * **leeway** (Time::Span) - Optional - Clock skew allowance for time-based claims validation. Defaults to 60.seconds. ### Examples **Basic JWKS validator (OIDC only):** ```crystal jwks = JWT::JWKS.new ``` **With local keys for service-to-service validation:** ```crystal jwks = JWT::JWKS.new( local_keys: { "service-key-1" => "secret-key-value", "service-key-2" => File.read("service_key.pem") }, local_algorithm: JWT::Algorithm::HS256 ) ``` **Custom cache TTL and leeway:** ```crystal jwks = JWT::JWKS.new( cache_ttl: 30.minutes, # Cache for 30 minutes instead of 10 leeway: 5.minutes # Allow 5 minutes clock skew ) ``` ``` -------------------------------- ### JWKS Constructor Signature Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md The JWT::JWKS class constructor accepts local keys, local algorithm, cache TTL, and leeway as parameters. ```crystal def initialize( @local_keys : Hash(String, String)? = nil, @local_algorithm : Algorithm? = nil, @cache_ttl : Time::Span = DEFAULT_CACHE_TTL, @leeway : Time::Span = DEFAULT_LEEWAY, ) ``` -------------------------------- ### Parse OIDC Metadata from HTTP Response Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/api-reference-oidc-metadata.md Demonstrates parsing OIDC metadata from an HTTP response body using the `from_json` method. This is common when fetching metadata from a provider's discovery endpoint. ```crystal # From HTTP response json_body = response.body metadata = JWT::JWKS::OIDCMetadata.from_json(json_body) ``` -------------------------------- ### Encode a JWT Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/00-START-HERE.md Use JWT.encode to create a JSON Web Token. Requires the payload, a secret key, and a signing algorithm. ```crystal token = JWT.encode(payload, key, JWT::Algorithm::HS256) ``` -------------------------------- ### Encode and Decode with Symmetric Key (HMAC) Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/INDEX.md Use this pattern for symmetric encryption where the same secret key is used for both encoding and decoding. Ensure the secret key is kept confidential. ```crystal secret = "my-secret-key" token = JWT.encode(payload, secret, JWT::Algorithm::HS256) payload, header = JWT.decode(token, secret, JWT::Algorithm::HS256) ``` -------------------------------- ### Default JWT Header Structure Source: https://github.com/crystal-community/jwt/blob/master/_autodocs/configuration.md Shows the default JSON structure for a JWT header, specifying the token type and algorithm. ```json { "typ": "JWT", "alg": "HS256" } ```