Class: Persona::Oidc::LinkStore

Inherits:
Object
  • Object
show all
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

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_tokenObject



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