Class: Verikloak::TokenDecoder

Inherits:
Object
  • Object
show all
Defined in:
lib/verikloak/token_decoder.rb

Overview

Verifies JWT tokens using a JWKS key set.

This class validates a JWT’s signature and standard claims (‘iss`, `aud`, `exp`, `nbf`, etc.) using the appropriate RSA public key selected by the JWT’s ‘kid` header. Only `RS256`-signed tokens with RSA JWKs are supported. It also supports a configurable clock skew (`leeway`) to account for minor time drift.

Examples:

decoder = Verikloak::TokenDecoder.new(
  jwks: jwks_keys,
  issuer: "https://keycloak.example.com/realms/myrealm",
  audience: "my-client-id",
  leeway: 30  # allow 30 seconds clock skew
)
payload = decoder.decode!(token)
puts payload["sub"]

Constant Summary collapse

DEFAULT_LEEWAY =

Default clock skew tolerance in seconds.

60

Instance Method Summary collapse

Constructor Details

#initialize(jwks:, issuer:, audience:, leeway: DEFAULT_LEEWAY) ⇒ TokenDecoder

Initializes the decoder with a JWKS and verification criteria.

Parameters:

  • jwks (Array<Hash>)

    List of JWKs from the discovery document.

  • issuer (String)

    Expected ‘iss` value in the token.

  • audience (String)

    Expected ‘aud` value in the token.

  • leeway (Integer) (defaults to: DEFAULT_LEEWAY)

    Clock skew tolerance in seconds (optional).



33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/verikloak/token_decoder.rb', line 33

def initialize(jwks:, issuer:, audience:, leeway: DEFAULT_LEEWAY)
  @jwks     = jwks
  @issuer   = issuer
  @audience = audience
  @leeway   = leeway

  # Build a kid-indexed hash for O(1) JWK lookup
  @jwk_by_kid = {}
  Array(@jwks).each do |j|
    kid_key = fetch_indifferent(j, 'kid')
    @jwk_by_kid[kid_key] = j if kid_key
  end
end

Instance Method Details

#decode!(token) ⇒ Hash

Decodes and verifies a JWT.

Parameters:

  • token (String)

    The JWT string to verify.

Returns:

  • (Hash)

    The decoded payload (claims).

Raises:

  • (TokenDecoderError)

    If verification fails. Possible error codes:

    • invalid_token

    • expired_token

    • not_yet_valid

    • invalid_issuer

    • invalid_audience

    • invalid_signature

    • unsupported_algorithm



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/verikloak/token_decoder.rb', line 59

def decode!(token)
  with_error_handling do
    # Extract header without verifying signature (payload is ignored here).
    header = JWT.decode(token, nil, false).last
    validate_header(header)                       # check alg and kid present
    jwk        = find_key_by_kid(header)          # locate JWK by kid
    public_key = rsa_key_from_jwk(jwk)            # import RSA public key
    payload = decode_with_public_key(token, public_key) # verify signature & claims
    payload
  end
end