Repository navigation
Conversation
…s email or phone Closes #475.
… feat/recovery-codes
A user can generate single-use codes and sign in with one; the session it issues can only enroll a new factor until one is enrolled.
…e' into feat/break-glass-admin
Accounts in BREAK_GLASS_EMAILS that hold admin:write are exempt from login policy for their own sign-in but need a passkey, TOTP or a recovery code. Every use that relied on the exemption is audited at high severity and emailed to every admin. Closes #401.
A locked account with no passkey, or one the policy leaves with no method, continues with a pre-auth token when it holds an unused recovery code.
…reak-glass-admin # Conflicts: # src/controllers/authentication.ts
…hange-notice # Conflicts: # src/controllers/admin.ts
# Conflicts: # docs/security-posture.md # resources/coverage-badge.svg # src/controllers/authentication.ts # src/services/sessionIssuance.ts
# Conflicts: # src/controllers/admin.ts # src/controllers/oauth.ts # src/controllers/otp.ts # src/services/sessionIssuance.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #401.
Depends on #491 (recovery codes, which merges #482 types 0.30.0 and #489 auth secrets) and #485 (admin identity change notice, for
sendNoticeEmail). This branch merges both, so the diff shrinks once they land.Behaviour
BREAK_GLASS_EMAILS(comma separated, trimmed, lower-cased) names admin accounts that sign-in policy cannot lock out. An entry counts only when the account exists, is not revoked and holds a role satisfyingadmin:write(hasScopedRole). Boot logs how many listed entries are not admin accounts, never the addresses.Where the exemption applies, for the account's own sign-in:
/login: adds the account's passkey whenlogin_methodsleaves passkeys out, and lets an account holding only TOTP or recovery codes continue (with a real pre-auth token) where it would otherwise get a decoy. The rest ofloginMethodsis shaped like any account's, so the answer does not single out a privileged account; an empty list (which only this account could produce) is never returned.POST /totp/verify-login: skips the passkey-required check (phishing_resistant_only, or passkey withpasskey_login_fallback_enabled: false). Lockout still applies.issueSessionAndRespond): passkey, TOTP and recovery code are allowed for the account in phishing-resistant-only mode.recovery_codes.enabledis false. Redeeming still issues the restricted recovery session.authenticator_policy.attachmentand theevaluateAuthenticatorPolicyverdict (synced passkeys, AAGUID allow/deny lists,requireKnownAuthenticator) do not refuse the account. The override is marked onregistration_successwithseverity: "high"andbreakGlassWaived.What it still needs: a passkey, TOTP or a recovery code. Email OTP and SMS OTP (send and verify), the verified-account path of
/otp/verify-email-otp, magic links (request and poll) and OAuth (finishOAuthLogin) answer403 login_method_disabled, and session issuance throwsBreakGlassFactorErrorfor those methods as a backstop.Audit and notices: a sign-in that relied on the exemption (the policy in force would otherwise have refused it) records
break_glass_sign_in(categorylogin). The audit model has no severity column, so the metadata carriesseverity: "high", plusfactor,waived(login_methods,phishing_resistant_only,passkey_required,recovery_codes_disabled) andnotices(sent,failed). Each non-revoked admin (admin:read or higher) with a verified email gets a notice viasendNoticeEmail. Notices go out after the session is issued and a failure never fails the sign-in.Shared helper:
src/config/breakGlass.tsexportsbreakGlassEmails(env = process.env)andisBreakGlassAdmin(user, env)(plusisBreakGlassConfigured), so #492'spolicyDryRun.tscan switch to the same parser at merge time.Not done: no
/health/statusfield. Its body is deliberately empty while healthy and schema-validated, so a boolean there is a contract change with no natural home.Contract
break_glass_sign_inis added to the auth event type enum (openapi.json/src/generated/api.ts). No route or response schema changes. Behaviour visible to clients: a break-glass account gets403 login_method_disabledfrom email/SMS OTP, magic link and OAuth even when they are enabled, and its/loginlist can include methods it is then refused.Checks
npm run typecheck: passnpm run lint: passnpx prettier --check .: passnpm run build: passnpm run coverage: pass, 148 files, 2091 tests passed (1 todo); statements 98.84%, branches 95.37%, functions 99.04%, lines 99.06%npm run generate:api: output committedsrcreverted (22 failures across the break-glass integration spec, the OAuth spec, the session issuance spec, and the two new unit specs, which do not load).