mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
docs: publish the Bearer arm on the two erasure routes
The published contract still told a client that a Bearer token can never clear the step-up on account deletion or the record wipe, which is what an app generates its client from. Describe the elevation arm on both: which factors qualify, that a password proof does not, that the proof rides the X-Step-Up header, and that it is spent only when the erasure runs. The 403 a narrow-scope token gets was never published either; it is named now, ahead of the second-factor question it precedes. The mint endpoint lists the surfaces an elevation unlocks, so both routes join that list. Also note on the record wipe that API tokens are on its own delete list: a client that wipes over Bearer signs itself out, which is worth knowing before the retry rather than after it.
This commit is contained in:
1 parent
5856b2a547
commit
df5dd1c160
4 files changed
+90
-20
No files matched your search
+55
-14
@@ -1989,8 +1989,15 @@ paths:
|
||||
Accepting routes (the complete set): POST /api/auth/me/mfa/totp/setup; POST /api/auth/me/mfa/totp/confirm; POST
|
||||
/api/auth/me/mfa/disable; POST /api/auth/me/mfa/recovery-codes/regenerate; POST
|
||||
/api/auth/me/mfa/webauthn/register/options; POST /api/auth/me/mfa/webauthn/register/verify; PATCH and DELETE
|
||||
/api/auth/me/mfa/webauthn/{id}. The last three of those and disable require a fresh-factor proof. GET
|
||||
/api/auth/me/mfa needs no elevation at all. Nothing else accepts one — admin endpoints stay cookie-only.
|
||||
/api/auth/me/mfa/webauthn/{id}; DELETE /api/settings/account; DELETE /api/settings/data. The last three MFA
|
||||
routes, disable, and both erasure routes require a fresh-factor proof. GET /api/auth/me/mfa needs no elevation
|
||||
at all. Nothing else accepts one — admin endpoints stay cookie-only.
|
||||
|
||||
|
||||
The two erasure routes joined the set so an account with a second factor can be deleted from a native app: their
|
||||
gate was cookie-only, which left an enrolled user unable to delete their own account from the app at all. They
|
||||
take the elevation the same way as the rest — fresh factor required, spent only when the erasure is about to
|
||||
run.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -11965,10 +11972,19 @@ paths:
|
||||
|
||||
|
||||
Two gates, and they compose. The body must carry the typed confirmation. And for an account with a second factor
|
||||
enrolled — a confirmed TOTP secret OR a registered security key — the session must additionally carry a fresh
|
||||
factor proof, which is cookie-only by construction: a Bearer transport carries no `mfaVerifiedAt` and therefore
|
||||
cannot satisfy it at any scope. An account with NO second factor keeps the typed-confirmation-only contract on
|
||||
either transport, deliberately, because a user who never enrolled has no ceremony to complete.
|
||||
enrolled — a confirmed TOTP secret OR a registered security key — the caller must additionally present a fresh
|
||||
factor proof. An account with NO second factor keeps the typed-confirmation-only contract on either transport,
|
||||
deliberately, because a user who never enrolled has no ceremony to complete.
|
||||
|
||||
|
||||
How the fresh proof is presented depends on the transport. A cookie session carries it on the session row:
|
||||
complete the second factor within the last five minutes and retry. A Bearer caller presents a single-use
|
||||
elevation from `POST /api/auth/step-up` in the `X-Step-Up` header, minted against a `totp`, `webauthn`, or
|
||||
`passkey` proof — a `password`-proved elevation is refused here exactly as a password login never satisfies
|
||||
step-up on the web. The elevation is bound to the token that minted it, valid five minutes, and spent only when
|
||||
the deletion is about to run, so a rejected confirmation does not burn it. A recovery code is not accepted on
|
||||
the Bearer path: the mint does not take one, so an account that has lost its authenticator deletes itself from
|
||||
the web.
|
||||
|
||||
|
||||
Two refusals are not about the caller's credentials at all. The last remaining admin cannot delete themselves,
|
||||
@@ -11993,9 +12009,20 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"401":
|
||||
description: Not authenticated — or the account has a second factor and the session carries no proof of it fresh enough
|
||||
for a destructive action (`meta.errorCode` = `auth.stepup.required`, or `auth.stepup.mfa_not_enrolled`).
|
||||
Re-verify the factor on the web and retry; a Bearer token cannot clear this arm.
|
||||
description: Not authenticated — or the account has a second factor and the caller carries no proof of it fresh enough
|
||||
for a destructive action (`meta.errorCode` = `auth.stepup.required`, or `auth.stepup.mfa_not_enrolled`). On
|
||||
a cookie session, re-verify the factor and retry; on a Bearer token, mint an elevation at `POST
|
||||
/api/auth/step-up` with a `totp`, `webauthn`, or `passkey` proof and resend with `X-Step-Up`. The same code
|
||||
covers an elevation that is unknown, expired, already spent, minted for a different token, or minted from
|
||||
too weak a factor — the refusals are deliberately indistinguishable on the wire, and the real reason is
|
||||
audited.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"403":
|
||||
description: The Bearer token carries a narrow scope. Erasure needs a cookie session or a full-access token; the refusal
|
||||
happens before any second-factor proof is looked at.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -12181,13 +12208,19 @@ paths:
|
||||
data.
|
||||
|
||||
|
||||
Gated like account deletion: the typed confirmation, plus a fresh factor proof for an MFA-enrolled account,
|
||||
which is cookie-only by construction. The confirmation string is `DELETE`, NOT the `DELETE_ACCOUNT` that route
|
||||
wants.
|
||||
Gated like account deletion: the typed confirmation, plus a fresh factor proof for an MFA-enrolled account — on
|
||||
a cookie session the second factor completed within the last five minutes, on a Bearer token a single-use
|
||||
`X-Step-Up` elevation minted at `POST /api/auth/step-up` against a `totp`, `webauthn`, or `passkey` proof. The
|
||||
confirmation string is `DELETE`, NOT the `DELETE_ACCOUNT` that route wants.
|
||||
|
||||
|
||||
One audit row survives the wipe that clears the rest of the audit history: it is written after the transaction
|
||||
commits, as the receipt for the erasure the person asked for.
|
||||
|
||||
|
||||
A Bearer client should expect to be signed out by its own request: API tokens are on the wipe list, so the token
|
||||
that called this — and any step-up elevation minted from it — is removed along with the record. Sign in again
|
||||
rather than retrying with the same credential.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -12202,9 +12235,17 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DataWipeEnvelope"
|
||||
"401":
|
||||
description: Not authenticated — or the account has a second factor and the session carries no proof of it fresh enough
|
||||
description: Not authenticated — or the account has a second factor and the caller carries no proof of it fresh enough
|
||||
for a destructive action (`meta.errorCode` = `auth.stepup.required`, or `auth.stepup.mfa_not_enrolled`). A
|
||||
Bearer token cannot clear this arm.
|
||||
Bearer caller clears it with an `X-Step-Up` elevation from `POST /api/auth/step-up`; the same code covers an
|
||||
elevation that is unknown, expired, already spent, bound to another token, or minted from too weak a factor.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"403":
|
||||
description: The Bearer token carries a narrow scope. Erasure needs a cookie session or a full-access token; the refusal
|
||||
happens before any second-factor proof is looked at.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
Reference in new issue
Block a user