# Verify a CodeB JWT locally in Ruby. # Signed with RS256 against the tenant JWKS. European Digital Identity # Wallet id_tokens are validated the same way. # [jwt-validation-cookbook 2026-07-23] # gem install jwt require 'jwt' require 'net/http' require 'json' require 'uri' ISSUER = 'https://' AUDIENCE = '' JWKS_URL = URI.parse("#{ISSUER}/.well-known/jwks.json") # In-memory JWKS cache with 10-minute TTL, refreshed on kid-miss. @jwks = nil @jwks_fetched = nil def fetch_jwks(force = false) if force || @jwks.nil? || (Time.now - @jwks_fetched > 600) @jwks = JSON.parse(Net::HTTP.get(JWKS_URL)) @jwks_fetched = Time.now end @jwks end def verify(token) JWT.decode(token, nil, true, { algorithms: ['RS256'], iss: ISSUER, verify_iss: true, aud: AUDIENCE, verify_aud: true, leeway: 5, jwks: proc { |opts| fetch_jwks(opts[:invalidate]) } }).first end if $PROGRAM_NAME == __FILE__ claims = verify(ARGV[0]) puts "sub = #{claims['sub']}" puts "role = #{claims['role'] || 'user'}" end # RFCs: 7519 (JWT), 7517 (JWK), 7518 (JWA).