Skip to content

fix(lockout): keep passkey sign-in out of lockout and enforce lockoutSeconds - #448

Merged
Bccorb merged 1 commit into
mainfrom
fix/lockout-per-factor
Oct 17, 2026
Merged

Bccorb merged 1 commit into
mainfrom
fix/lockout-per-factor

Conversation

@Bccorb

@Bccorb Bccorb commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Fix for GHSA-66gq-4pwg-33jq. Reviewed privately in the advisory fork.

What changes

  • Passkey sign-in is not subject to lockout, and failed assertions no longer count (webauthn_login_failed leaves LOCKOUT_FAILURE_TYPES). An assertion cannot be guessed.
  • POST /login for a locked account continues with loginMethods: ['passkey'] when the account has one, and answers 423 only when it has none.
  • The lock starts at the failure that brings maxFailures inside one windowSeconds and lasts lockoutSeconds from there; retryAfterSeconds is the time actually left. The counter now reads the newest maxFailures failures, filtered by type, so old passkey failures stop counting. Unreadable history still means locked.
  • Minor changeset. Not a contract change for clients: loginMethods and the 423 body keep their shapes.

Checks

  • typecheck, lint, format: pass
  • npm run coverage: 136 files, 1818 passed, 1 todo. Statements 98.91%, branches 95.38%, functions 99.27%, lines 99.14%
  • The new failure query run against Postgres 16 with seeded rows: returns code-factor failures newest first, excludes passkey failures, honours the limit

Residual

An account with no passkey can still be locked by repeated wrong codes, which is what lockout is for. Recovery for that case is v0.20 (#398, #399, #401).

Part of the v0.19 security release (#385). Rebased onto main after #440 to #446; 138 files, 1862 tests pass locally.

…Seconds

Failed code-factor attempts, which anyone with an ephemeral token can make,
no longer lock an account out of its passkeys. A locked account continues
with its passkey from /login. The lock lasts lockoutSeconds from the
failure that crossed the threshold, and retryAfterSeconds reports the time
actually left.
@Bccorb
Bccorb merged commit 14ebadf into main Oct 17, 2026
7 checks passed
@Bccorb
Bccorb deleted the fix/lockout-per-factor branch October 17, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant