4.1 KiB
4.1 KiB
Auth, Revoke, and Repair Flows
Public Auth Flow
sequenceDiagram
participant Client
participant Gateway
participant Auth
participant Abuse as Resend throttle
participant User as UserDirectory
participant Mail as MailSender
participant Challenge as ChallengeStore
participant Session as SessionStore
participant Config as ConfigProvider
participant Projection as Gateway projection publisher
Client->>Gateway: POST /api/v1/public/auth/send-email-code
Gateway->>Auth: POST /api/v1/public/auth/send-email-code
Auth->>Abuse: check and reserve cooldown
alt throttled
Abuse-->>Auth: throttled
Auth->>Challenge: create delivery_throttled challenge
Auth-->>Gateway: 200 {challenge_id}
else allowed
Abuse-->>Auth: allowed
Auth->>User: ResolveByEmail(email)
User-->>Auth: existing / creatable / blocked
Auth->>Challenge: create pending challenge
alt blocked
Auth->>Challenge: mark delivery_suppressed
else not blocked
Auth->>Mail: SendLoginCode(email, code)
Mail-->>Auth: sent / suppressed / failure
Auth->>Challenge: persist final delivery outcome
end
Auth-->>Gateway: 200 {challenge_id}
end
Client->>Gateway: POST /api/v1/public/auth/confirm-email-code
Gateway->>Auth: POST /api/v1/public/auth/confirm-email-code
Auth->>Challenge: load and validate challenge
Auth->>User: EnsureUserByEmail(email, registration_context)
User-->>Auth: existing / created / blocked
Auth->>Config: LoadSessionLimit()
Auth->>Session: CountActiveByUserID(user_id)
Auth->>Session: create device session
Auth->>Challenge: CAS to confirmed_pending_expire
Auth->>Session: reread current stored session view
Auth->>Projection: publish gateway snapshot
Auth-->>Gateway: 200 {device_session_id}
Revoke and Block Flow
sequenceDiagram
participant Caller as Trusted internal caller
participant Auth
participant User as UserDirectory
participant Session as SessionStore
participant Projection as Gateway projection publisher
participant Gateway
Caller->>Auth: revoke or block request
alt block by user or email
Auth->>User: apply block mutation
User-->>Auth: blocked / already_blocked
end
Auth->>Session: revoke one or many sessions
Session-->>Auth: updated source-of-truth sessions
loop each affected session
Auth->>Projection: publish revoked snapshot
end
Auth-->>Caller: 200 acknowledgement
Projection-->>Gateway: revoked session snapshot
Projection Repair On Retry
Projection writes happen after source-of-truth updates. If projection publish
fails after state is already stored, the caller sees service_unavailable, and
the repair path is to repeat the same request.
sequenceDiagram
participant Client
participant Auth
participant Challenge as ChallengeStore
participant Session as SessionStore
participant Projection as Gateway projection publisher
Client->>Auth: confirm-email-code
Auth->>Challenge: validate challenge
Auth->>Session: create session
Auth->>Challenge: persist confirmed_pending_expire
Auth->>Projection: publish snapshot
Projection-->>Auth: failure
Auth-->>Client: 503 service_unavailable
Client->>Auth: repeat same confirm-email-code
Auth->>Challenge: load confirmed_pending_expire challenge
Auth->>Session: load stored session from confirmation metadata
Auth->>Projection: republish current stored session view
Projection-->>Auth: success
Auth-->>Client: 200 {device_session_id}
Confirm-Race Cleanup
Concurrent identical confirms are allowed to race at the store level, but the service converges them back to one surviving active session.
- the winning CAS stores challenge confirmation metadata and publishes the surviving session snapshot
- a superseded session created by a losing racing request is revoked
best-effort with
reason_code=confirm_race_repair - cleanup uses the same projection helper, but cleanup failure is not part of the caller-visible success contract