Class: Keycardai::OAuth::JWTVerifier
- Inherits:
-
Object
- Object
- Keycardai::OAuth::JWTVerifier
- Defined in:
- lib/keycardai/oauth/jwt_verifier.rb
Overview
Verifies compact JWTs (RFC 7519) against the RFC 9068 access-token profile, fail-closed. Cheap policy checks (algorithm, trusted issuer, required claims, expiry, audience, kid) run before any key resolution, so a token carrying an attacker-controlled iss never drives key lookup or network I/O. Verification keys are resolved by (issuer, kid) through the injected keyring (see JWKSKeyring).
Every rejection raises InvalidTokenError; misconfiguration raises ConfigurationError at construction.
Constant Summary collapse
- SUPPORTED_ALGORITHMS =
["RS256"].freeze
- REQUIRED_CLAIMS =
%w[iss sub aud exp iat client_id].freeze
Instance Method Summary collapse
-
#initialize(issuers:, keyring:, audiences: nil, algorithms: SUPPORTED_ALGORITHMS, clock_skew: 0, clock: -> { Time.now }) ⇒ JWTVerifier
constructor
A new instance of JWTVerifier.
-
#verify(token) ⇒ Hash
Verify a compact JWT and return its claim set.
Constructor Details
#initialize(issuers:, keyring:, audiences: nil, algorithms: SUPPORTED_ALGORITHMS, clock_skew: 0, clock: -> { Time.now }) ⇒ JWTVerifier
Returns a new instance of JWTVerifier.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/keycardai/oauth/jwt_verifier.rb', line 30 def initialize(issuers:, keyring:, audiences: nil, algorithms: SUPPORTED_ALGORITHMS, clock_skew: 0, clock: -> { Time.now }) @issuers = Array(issuers).reject { |issuer| issuer.nil? || issuer.empty? } raise ConfigurationError, "JWTVerifier requires at least one trusted issuer" if @issuers.empty? unimplemented = Array(algorithms) - SUPPORTED_ALGORITHMS raise ConfigurationError, "unimplemented algorithms: #{unimplemented.join(", ")}" unless unimplemented.empty? @keyring = keyring @audiences = audiences.nil? ? nil : Array(audiences) @algorithms = Array(algorithms) @clock_skew = clock_skew @clock = clock end |
Instance Method Details
#verify(token) ⇒ Hash
Verify a compact JWT and return its claim set.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/keycardai/oauth/jwt_verifier.rb', line 51 def verify(token) header, claims, signature, signing_input = decode_parts(token) check_algorithm(header) check_issuer(claims) check_required_claims(claims) check_temporal(claims) check_audience(claims) kid = header["kid"] raise InvalidTokenError, "token header has no kid" if kid.nil? || kid.empty? key = @keyring.key(claims["iss"], kid) check_signature(key, signature, signing_input) claims end |