mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
feat(auth): let a single-sign-on account confirm it is you at its provider
An account created through OIDC holds no password, passkey or second factor, so the recent-proof gate in front of token minting, MCP connections, exports and the admin reset had nothing to ask it for and the web could only say "sign in again" (#1296). GET /api/auth/oidc/reproof sends the browser back to the configured provider with prompt=login and max_age=0, its own state, nonce and PKCE verifier, and the signed-in account and session sealed into the state cookie. The shared callback branches on that intent: it refuses unless the browser still carries the bound session (checked before the code is redeemed), the ID token names the account's linked issuer and subject, and auth_time lies inside the gate's five-minute window. It then stamps Session.reproofAt exactly as a password re-proof does, creates no session, and returns to the page it left with ?reproof=done|failed. recentProofMethods offers "oidc" only to an account without a second factor whose identity is linked while a provider is configured, and the fresh-factor gate never lists it. GET /api/auth/me/mfa publishes the list as proofMethods, so the passkey card offers what the account can actually give, and register-options takes { method: "oidc" } on a fresh provider re-proof, so such an account can add a passkey as a second way to confirm. Both re-proof dialogs offer the provider and a toast reports the outcome on return. The callback's missing-state branch and the passkey card's enrollment flow move into their own functions to keep both handlers under the size ratchet.
This commit is contained in:
1 parent
35a0ceb12c
commit
bbef4eea99
35 files changed
+1654
-187
No files matched your search
+36
-5
@@ -2481,10 +2481,13 @@ paths:
|
||||
|
||||
The body takes the same shapes as POST /api/auth/step-up. On an account with a second factor only `totp`,
|
||||
`webauthn` (a security key) or `passkey` are accepted, because a password has never stood in for the second
|
||||
factor; on one without, `password` or `passkey`. A method the account cannot use here is refused with 422
|
||||
`auth.reproof.too_weak` and `meta.methods`, before any guess is spent. A second-factor or passkey proof also
|
||||
refreshes the session's second-factor stamp, exactly as signing in again would. Assertions begin at POST
|
||||
/api/auth/passkey/register-options with `{ method: "passkey" | "webauthn" }`.
|
||||
factor; on one without, `password` or `passkey`. An account without a second factor whose identity is linked to
|
||||
the configured identity provider is also offered `oidc` in `meta.methods`; that proof is not posted here but
|
||||
given by the browser round trip GET /api/auth/oidc/reproof, which stamps the session exactly as a password proof
|
||||
does. A method the account cannot use here is refused with 422 `auth.reproof.too_weak` and `meta.methods`,
|
||||
before any guess is spent. A second-factor or passkey proof also refreshes the session's second-factor stamp,
|
||||
exactly as signing in again would. Assertions begin at POST /api/auth/passkey/register-options with `{ method:
|
||||
"passkey" | "webauthn" }`.
|
||||
|
||||
|
||||
Proofs draw on the account's shared re-proof budget (five per fifteen minutes, 429 once spent). A proof that
|
||||
@@ -4077,7 +4080,9 @@ paths:
|
||||
|
||||
**Enrollment arm.** Any other body must be a full factor proof (the same shape POST /api/auth/step-up takes): a
|
||||
password, a TOTP code, or a completed passkey / security-key assertion. Only then are registration options
|
||||
issued. On an account that already has a second factor a password is refused before it is checked, with 422
|
||||
issued. An account confirmed at its identity provider (GET /api/auth/oidc/reproof, within five minutes) sends `{
|
||||
method: "oidc" }` instead, which takes that re-proof as the proof and, like a password, clears the second-factor
|
||||
stamp. On an account that already has a second factor a password is refused before it is checked, with 422
|
||||
`auth.reproof.too_weak` and `meta.methods` naming the proofs that work: a passkey added on the password alone
|
||||
would sign in without the second factor from then on.
|
||||
|
||||
@@ -4103,6 +4108,7 @@ paths:
|
||||
schema:
|
||||
anyOf:
|
||||
- $ref: "#/components/schemas/PasskeyReauthRequest"
|
||||
- $ref: "#/components/schemas/PasskeyProviderProofRequest"
|
||||
- $ref: "#/components/schemas/StepUpMintRequest"
|
||||
responses:
|
||||
"200":
|
||||
@@ -28611,6 +28617,17 @@ components:
|
||||
description: The re-proof arm of register-options. Recognised ONLY when `method` is the body's single key — adding any
|
||||
other property routes the request to the enrollment arm, where it will be read as a factor proof and almost
|
||||
certainly refused.
|
||||
PasskeyProviderProofRequest:
|
||||
type: object
|
||||
properties:
|
||||
method:
|
||||
type: string
|
||||
const: oidc
|
||||
required:
|
||||
- method
|
||||
description: Take the session's recent identity-provider re-proof (GET /api/auth/oidc/reproof) as the proof. Accepted
|
||||
only for an account whose `proofMethods` list `oidc` and only within five minutes of that re-proof; otherwise
|
||||
401 `auth.reproof.required` with `meta.methods`. Web only.
|
||||
PasskeyRegisterVerifyRequest:
|
||||
type: object
|
||||
properties:
|
||||
@@ -47232,11 +47249,25 @@ components:
|
||||
$ref: "#/components/schemas/WebauthnMfaCredentialInfo"
|
||||
passkeyNudgeDismissed:
|
||||
type: boolean
|
||||
proofMethods:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- password
|
||||
- oidc
|
||||
- totp
|
||||
- webauthn
|
||||
- passkey
|
||||
description: The ways this account can confirm it is you, the same list a recent-proof refusal names in `meta.methods`.
|
||||
`password` and `oidc` only on an account without a second factor; `oidc` is a browser round trip to the
|
||||
identity provider (`GET /api/auth/oidc/reproof`) and is never offered to a token.
|
||||
required:
|
||||
- totp
|
||||
- recoveryCodesRemaining
|
||||
- webauthn
|
||||
- passkeyNudgeDismissed
|
||||
- proofMethods
|
||||
additionalProperties: false
|
||||
WebauthnMfaCredentialInfo:
|
||||
type: object
|
||||
|
||||
Reference in new issue
Block a user