Module: LcpRuby::Authentication::BearerJwtVerifier
- Defined in:
- lib/lcp_ruby/authentication/bearer_jwt_verifier.rb
Overview
Pure JWT validator for bearer authentication. Stateless; every call re-runs shape, signature, and claim invariants. Returns ‘[claims_hash, provider]` on success and `nil` otherwise — no exceptions surface to the caller.
Constant Summary collapse
- CLOCK_SKEW =
seconds
60
Class Method Summary collapse
Class Method Details
.verify(token_string) ⇒ Array(Hash, Provider)?
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/lcp_ruby/authentication/bearer_jwt_verifier.rb', line 16 def verify(token_string) return nil if token_string.nil? || token_string.to_s.empty? unverified = unverified_decode(token_string) return nil unless unverified header = unverified[:header] claims = unverified[:claims] return nil unless header["alg"] == "RS256" # RFC 7515 §4.1.11 — "crit" lists header params the verifier MUST # understand. We support none, so any non-empty crit array, or any # non-array crit value, forces reject. crit = header["crit"] return nil unless crit.nil? || (crit.is_a?(Array) && crit.empty?) kid = header["kid"].to_s return nil if kid.empty? provider = find_provider_by_issuer(claims["iss"]) return nil unless provider return nil if provider.audience.to_s.empty? jwk = JwksCache.key_for(provider, kid: kid) return nil unless jwk verified = verify_signature(token_string, jwk) return nil unless verified verified_claims = verified.to_h.transform_keys(&:to_s) return nil unless claim_invariants_pass?(verified_claims, provider) [ verified_claims, provider ] rescue StandardError => e # Last-resort guard — preserves "all errors → nil" contract while # surfacing the cause in logs (PII-scrubbed, no raw token). Rails.logger.info( "[lcp_ruby] BearerJwtVerifier rejected token: #{e.class}: #{e.}" ) if defined?(Rails) nil end |