Class: Persona::Oidc::Verifier
- Inherits:
-
Object
- Object
- Persona::Oidc::Verifier
- Defined in:
- lib/persona/oidc/verifier.rb
Overview
Production OIDC verifier provider (docs/spec §7, P-16): the authorization-code + PKCE flow, OIDC discovery, code exchange, and id_token validation against the IdP's published JWKS. Mirrors the TypeScript createOidcVerifierProvider.
Signature verification uses the jwt gem's OpenSSL backend and covers RS256 and ES256. EdDSA (Ed25519) is intentionally not enabled here — it needs rbnacl in Ruby — so an IdP signing with EdDSA must expose an RS256/ES256 key or wait for that follow-up.
The clock (#now) is injectable so expiry is checked here rather than by the jwt gem, matching the TS verifier and keeping tests deterministic.
Constant Summary collapse
- ALLOWED_ALGS =
%w[RS256 ES256].freeze
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Class Method Summary collapse
-
.uniba_auth(issuer:, client_id:, redirect_uri:, client_secret: nil, allowed_email_domain: 'uniba.jp', http: NetHttpClient.new, now: -> { Time.now.to_i }) ⇒ Object
Preset for uniba/auth (github.com/uniba/auth): the uniba.jp shared OIDC AS.
Instance Method Summary collapse
- #authorize(state) ⇒ Object
-
#initialize(name:, issuer:, client_id:, redirect_uri:, client_secret: nil, scope: 'openid email', authorization_endpoint: nil, token_endpoint: nil, jwks_uri: nil, authorize_params: {}, require_hosted_domain: nil, allowed_email_domain: nil, http: NetHttpClient.new, now: -> { Time.now.to_i }, clock_tolerance: 60) ⇒ Verifier
constructor
A new instance of Verifier.
- #verify(params, stash = nil) ⇒ Object
Constructor Details
#initialize(name:, issuer:, client_id:, redirect_uri:, client_secret: nil, scope: 'openid email', authorization_endpoint: nil, token_endpoint: nil, jwks_uri: nil, authorize_params: {}, require_hosted_domain: nil, allowed_email_domain: nil, http: NetHttpClient.new, now: -> { Time.now.to_i }, clock_tolerance: 60) ⇒ Verifier
Returns a new instance of Verifier.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/persona/oidc/verifier.rb', line 53 def initialize( name:, issuer:, client_id:, redirect_uri:, client_secret: nil, scope: 'openid email', authorization_endpoint: nil, token_endpoint: nil, jwks_uri: nil, authorize_params: {}, require_hosted_domain: nil, allowed_email_domain: nil, http: NetHttpClient.new, now: -> { Time.now.to_i }, clock_tolerance: 60 ) @name = name @issuer = issuer.to_s.sub(%r{/\z}, '') @client_id = client_id @redirect_uri = redirect_uri @client_secret = client_secret @scope = scope @explicit_endpoints = { authorization_endpoint: , token_endpoint: token_endpoint, jwks_uri: jwks_uri } @authorize_params = @require_hosted_domain = require_hosted_domain @allowed_email_domain = allowed_email_domain @http = http @now = now @clock_tolerance = clock_tolerance @discovery = nil @jwks = nil end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
51 52 53 |
# File 'lib/persona/oidc/verifier.rb', line 51 def name @name end |
Class Method Details
.uniba_auth(issuer:, client_id:, redirect_uri:, client_secret: nil, allowed_email_domain: 'uniba.jp', http: NetHttpClient.new, now: -> { Time.now.to_i }) ⇒ Object
Preset for uniba/auth (github.com/uniba/auth): the uniba.jp shared OIDC AS. Fills in the name, scope, and hd=uniba.jp upstream constraint; the issuer and client credentials stay per-deployment. auth is not yet published, so the token-claim domain check defaults to the email domain (provisional). Pass allowed_email_domain: nil to disable it.
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/persona/oidc/verifier.rb', line 83 def self.uniba_auth(issuer:, client_id:, redirect_uri:, client_secret: nil, allowed_email_domain: 'uniba.jp', http: NetHttpClient.new, now: -> { Time.now.to_i }) new( name: 'uniba-auth', issuer: issuer, client_id: client_id, redirect_uri: redirect_uri, client_secret: client_secret, scope: 'openid email', authorize_params: { hd: 'uniba.jp' }, allowed_email_domain: allowed_email_domain, http: http, now: now, ) end |
Instance Method Details
#authorize(state) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/persona/oidc/verifier.rb', line 94 def (state) code_verifier = SecureRandom.urlsafe_base64(32) nonce = SecureRandom.urlsafe_base64(16) query = { response_type: 'code', client_id: @client_id, redirect_uri: @redirect_uri, scope: @scope, state: state, nonce: nonce, code_challenge: base64url(Digest::SHA256.digest(code_verifier)), code_challenge_method: 'S256', }.merge(@authorize_params) url = "#{discovery.fetch(:authorization_endpoint)}?#{URI.encode_www_form(query)}" AuthorizeStart.new(url: url, stash: { code_verifier: code_verifier, nonce: nonce }) end |
#verify(params, stash = nil) ⇒ Object
107 108 109 110 111 112 113 114 115 116 |
# File 'lib/persona/oidc/verifier.rb', line 107 def verify(params, stash = nil) code = params[:code] || params['code'] return nil if code.nil? || code.to_s.empty? tokens = exchange_code(code, stash) id_token = tokens && (tokens['id_token'] || tokens[:id_token]) return nil if id_token.nil? validate_id_token(id_token, stash) end |