Class: Persona::Oidc::LinkStore
- Inherits:
-
Object
- Object
- Persona::Oidc::LinkStore
- Defined in:
- lib/persona/oidc/link_store.rb
Overview
Short-lived Redis storage that ties the OIDC redirect round-trip to the browser that started it — without ever putting the agent_uid in a URL.
begin : put_pending(state, agent_uid:, provider:)
/auth/oidc/callback : take_pending(state) -> {agent_uid:, provider:}
(single use)
put_result(link_token, ...) hands the
verified subject
back to the app
complete : peek_result(link_token) -> {...} (non-destructive,
so the merge
confirm can re-read)
drop_result(link_token) on final success
The Redis dependency is injected: pass either a raw client (responds to #get / #set / #del) or a connection pool (responds to #with yielding a client). Consumers register the instance via Persona.config.link_store.
Constant Summary collapse
- PENDING_PREFIX =
'persona:oidc:pending:'.freeze
- RESULT_PREFIX =
'persona:oidc:result:'.freeze
- PENDING_TTL =
10 min to complete the IdP round-trip
600- RESULT_TTL =
5 min to finish the client-side confirm
300
Instance Method Summary collapse
- #drop_result(link_token) ⇒ Object
- #generate_token ⇒ Object
-
#initialize(redis:) ⇒ LinkStore
constructor
A new instance of LinkStore.
-
#peek_result(link_token) ⇒ Object
Non-destructive: the merge-preview first call must leave the token valid for the confirm call.
-
#put_pending(state, agent_uid:, provider:, stash: nil) ⇒ Object
provider records which registered IdP this round-trip was begun for, so the callback verifies against the same provider (multi-provider deployments; docs/spec §7).
- #put_result(link_token, provider:, subject:, agent_uid:) ⇒ Object
-
#take_pending(state) ⇒ Object
Consumes the pending entry (state is single-use, CSRF-style).
Constructor Details
#initialize(redis:) ⇒ LinkStore
Returns a new instance of LinkStore.
29 30 31 |
# File 'lib/persona/oidc/link_store.rb', line 29 def initialize(redis:) @redis = redis end |
Instance Method Details
#drop_result(link_token) ⇒ Object
64 65 66 |
# File 'lib/persona/oidc/link_store.rb', line 64 def drop_result(link_token) with_redis { |conn| conn.del(RESULT_PREFIX + link_token) } end |
#generate_token ⇒ Object
33 34 35 |
# File 'lib/persona/oidc/link_store.rb', line 33 def generate_token SecureRandom.urlsafe_base64(32) end |
#peek_result(link_token) ⇒ Object
Non-destructive: the merge-preview first call must leave the token valid for the confirm call.
60 61 62 |
# File 'lib/persona/oidc/link_store.rb', line 60 def peek_result(link_token) read(RESULT_PREFIX + link_token) end |
#put_pending(state, agent_uid:, provider:, stash: nil) ⇒ Object
provider records which registered IdP this round-trip was begun for, so the callback verifies against the same provider (multi-provider deployments; docs/spec §7). stash carries the provider's per-flow secrets (PKCE code_verifier, nonce) from authorize to verify.
41 42 43 |
# File 'lib/persona/oidc/link_store.rb', line 41 def put_pending(state, agent_uid:, provider:, stash: nil) write(PENDING_PREFIX + state, { agent_uid: agent_uid, provider: provider, stash: stash }, PENDING_TTL) end |
#put_result(link_token, provider:, subject:, agent_uid:) ⇒ Object
50 51 52 53 54 55 56 |
# File 'lib/persona/oidc/link_store.rb', line 50 def put_result(link_token, provider:, subject:, agent_uid:) write( RESULT_PREFIX + link_token, { provider: provider, subject: subject, agent_uid: agent_uid }, RESULT_TTL, ) end |
#take_pending(state) ⇒ Object
Consumes the pending entry (state is single-use, CSRF-style).
46 47 48 |
# File 'lib/persona/oidc/link_store.rb', line 46 def take_pending(state) take(PENDING_PREFIX + state) end |