mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-11-13 12:24:46 +00:00
* feat: passkey authentication with WebAuthn sign-in and management
* refactor: tighten passkey account protections
Require password confirmation before adding or removing a passkey, and rate
limit those confirmations per user in their own namespace. Restrict passkey
enrollment and sign-in to local accounts, and re-evaluate account status once
the credential owner is resolved. Require user verification on the sign-in
ceremony. Invalidate outstanding access tokens on password reset. Tighten
redirect target validation and sanitize ceremony failure logs.
Adds coverage across the passkey controller, the WebAuthn helpers, the step-up
limiter, both JWT strategies, the redirect utility and the passkey settings UI.
* fix: localize passkey default name and respect router basename
Send a localized default credential label at enrollment so non-English
locales no longer persist the server's English fallback, and route the
passkey post-login and 2FA navigations through the deployment base path
so subdirectory installs stay inside the router.
Also mock syncStaticTools in the server boot specs; without it the
post-listen MCP initialization calls undefined and exits the worker.
* fix: harden passkey counter checks and reach stranded credentials
Make the signature counter write a compare-and-swap so two assertions
that verified against the same stored counter cannot both commit, and
reject the losing one; a cloned authenticator is otherwise accepted
inside that window. Counterless authenticators keep restamping at 0.
Keep the passkey panel reachable for identity-provider accounts, which
already get passwordless removal server-side but had no way in, while
hiding the add control they cannot use. Cascade passkey deletion from
the admin user-deletion path so credential IDs are not stranded.
* fix: reject stale 2FA temp tokens and surface passkey load failures
A temporary 2FA token proves only the first factor, so honouring one
minted before a password reset let anyone holding the second factor
survive account recovery. Apply the same credentials-changed cutoff the
JWT strategy uses.
Also distinguish a failed passkey query from an empty list; the error
state previously rendered as "no passkeys yet", which misreports what
is protecting the account.
* fix: fail closed when the passkey counter write cannot commit
Returning success on a storage error bypassed the compare-and-swap it
exists to enforce, so a cloned authenticator could be accepted while the
stored counter stayed stale. A database that cannot take this write
cannot mint the session either, so little is gained by failing open.
Counterless credentials carry no clone signal and keep their old path.
* fix: keep the passkey 2FA handoff inside the router
A full-page navigation to the 2FA screen remounted AuthContextProvider,
which silent-refreshes on mount; no refresh session exists yet at that
point, so it bounced back to login before the code could be entered.
Route through the router like the password flow does.
Also reveal the removal step-up field when the server refuses a
passwordless attempt, so an account migrated to an identity provider
without its old hash cleared can still clear its credentials; drop the
staged password when the dialog closes; and revoke passkeys in the CLI
password reset, which bypassed the HTTP controller's cleanup.
* fix: apply one email-verification policy across login methods
Accounts predating mandatory verification are marked verified as they
sign in when no email is configured, but only the password strategy did
it, so the same legacy account was admitted by password and refused by
passkey. Extract the rule into a shared helper and call it from both.
* fix: refuse a passkey sign-in when the ban check cannot complete
checkBan defaults next to a no-op, so calling it bare discarded any
internal failure and left req.banned unset, letting the request fall
through and be issued tokens. Capture the error the middleware chain
would have propagated and reject instead.
* fix: refine passkey management dialogs
* test: stabilize passkey tooltip assertions
* test: reconcile passkey specs with the canary reset and deletion flows
The credentialsChangedAt stamp now lives in commitPasswordReset, so its
assertion moves to the passwordReset unit spec. Restores the describe
blocks the rebase merged together.
* test: mock passkey routes in the cross-site login suite
* refactor: move passkey handlers and credential checks into packages/api
The passkey controller, the credentialsChangedAt predicate and the legacy
email-verification grandfathering now live in TypeScript with injected
dependencies; /api keeps only the wiring that builds the handlers.
* style: align passkey UI with Tailwind v4 order and design-rule lint
* fix: revoke earlier access tokens on a CLI password reset
passwordVersion is not a user schema field, so the write was dropped and
tokens issued before the reset kept verifying.
* test: cover passkey sign-in and credential revocation end to end
* fix: hide passkey sign-in when email login is disabled
The passkey endpoints already refuse under ALLOW_EMAIL_LOGIN=false, so
advertising the button only led to a 403.
* fix: let only one concurrent verification consume a passkey challenge
Without a native getDel the challenge was read then deleted, so two
concurrent verifications could both use it. The delete now arbitrates:
only the caller whose delete removed the key gets the challenge.
* test: run passkey ceremonies on the localhost relying party
WebAuthn refuses an IP address as the RP ID, and the harness serves the
app on 127.0.0.1. The reset scenario runs once per project, so the mock
harness lifts the per-IP reset request limit as it does for forks.
* fix: keep passkey management when email login is disabled
Gate only the sign-in button on emailLoginEnabled, since only the sign-in
routes sit behind that gate; list, rename and remove stay reachable.
* fix: build the passkey credential index before the first registration
With MONGO_AUTO_INDEX=false the unique credentialId index was never
created, so concurrent registrations could store the same credential.
* test: enable password reset in the mock harness and cover email-login-off passkeys
* fix: stop re-exporting the passkey cap from data-schemas
The bundler left every import of methods/passkey.ts unlinked while that
module re-exported MAX_PASSKEYS_PER_USER from librechat-data-provider, so
the index build and the counter-failure log threw ReferenceError at
runtime. Consumers now import the cap from librechat-data-provider.
* test: close nested settings dialogs before logging out in passkey scenarios
* fix: reject cross-site passkey assertions
The verify route accepted a form posted from another site, which let an
attacker sign a victim into the attacker's account. It now runs the same
requireSameOrigin guard as password login and the 2FA step.
* fix: end refresh sessions on a CLI password reset
A surviving refresh session could mint an access token issued after the
credentialsChangedAt stamp, so the CLI now deletes sessions like the web
reset does.
* fix: keep the passkey button locked when it supersedes autofill
Starting the modal ceremony aborts the pending autofill one, and the
autofill error path cleared the shared lock and busy state while the
modal ceremony was still running.
(cherry picked from commit
|
||
|---|---|---|
| .. | ||
| banViolation.js | ||
| banViolation.spec.js | ||
| clearPendingReq.js | ||
| getLogStores.js | ||
| index.js | ||
| logViolation.js | ||