Add WebAuthn/Passkey authentication support#5267
Open
florida117 wants to merge 11 commits intoNginxProxyManager:developfrom
Open
Add WebAuthn/Passkey authentication support#5267florida117 wants to merge 11 commits intoNginxProxyManager:developfrom
florida117 wants to merge 11 commits intoNginxProxyManager:developfrom
Conversation
9ea04e4 to
a3fe8c6
Compare
Implements passwordless passkey login alongside the existing email/password flow. Passkeys provide phishing-resistant, multi-factor authentication (device + biometric) and bypass TOTP 2FA when used. Closes NginxProxyManager#3363 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BearerAuth -> bearerAuth to match the scheme defined in components/security-schemes.json. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add missing parameter descriptions and examples for userID/passkeyID path parameters, add response/request examples to all passkey endpoint schemas, and suppress false-positive no-http-verbs-in-path warnings for WebAuthn "options" endpoints via x-lint-ignore. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Setup page now uses a three-step flow: enter name/email, choose between password or passkey, then complete the chosen method. The backend returns a JWT token in setup mode so the frontend can authenticate immediately without a separate login call. Also fix WebAuthn origin mismatch by preferring the Origin request header over constructing it from protocol/host, which dropped the port in proxied environments. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend now returns has_password field when fetching user data, allowing the frontend to distinguish between password and passkey-only accounts. The change password modal hides the current password field and shows "Set Password" for accounts without a password. The backend setPassword handler skips the current password check when no password auth exists. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users with at least one registered passkey can now remove their password to go fully passwordless. Backend validates passkey existence before allowing removal, and the frontend shows a "Remove Password" button in the Change Password modal when both a password and passkeys are present. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The custom omissions early return in internalUser.get() was placed before the has_password assignment, preventing it from ever being set on the response. This caused the frontend to always default to showing the "current password" field when changing passwords. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
754664b to
01b4da8
Compare
…ing example The POST /users endpoint returns token and expires properties during initial setup, but the User schema had additionalProperties: false without defining them, causing 7 Cypress test failures. Also adds missing example to has_password to fix SwaggerSchema lint warning. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Docker Image for build 10 is available on DockerHub: Note Ensure you backup your NPM instance before testing this image! Especially if there are database changes. Warning Changes and additions to DNS Providers require verification by at least 2 members of the community! |
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.
Summary
Implements passwordless passkey (WebAuthn) authentication for the admin interface, as requested in #3363.
Configuration
WebAuthn relying party settings are auto-detected from the request. When behind a reverse proxy, the standard
X-Forwarded-HostandX-Forwarded-Protoheaders are used (Expresstrust proxyis already enabled).Environment variables are available as optional overrides if needed:
WEBAUTHN_RP_IDreq.hostname(auto-detected)WEBAUTHN_RP_NAMENginx Proxy ManagerWEBAUTHN_ORIGINreq.protocol + req.get("host")(auto-detected)Passkeys registered under one origin will not work from a different origin — this is by design in the WebAuthn spec.
Backend changes
webauthn_credentialdatabase table via Knex migrationbackend/models/webauthn_credential.js)backend/internal/webauthn.js) handling registration, authentication, listing, renaming, and deletiongetTokenFromPasskey()added tobackend/internal/token.jsPOST /tokens/passkey/options,POST /tokens/passkey/verifyGET/POST/PUT/DELETE /users/:id/passkeys/...Frontend changes
@simplewebauthn/browserintegration inAuthContext(loginWithPasskey)PasskeyModalfor managing passkeys (register, list, rename, delete)SiteHeaderreact-intllocale stringsDependencies added
@simplewebauthn/server@^11.0.0@simplewebauthn/browser@^11.0.0Test plan
npx knex migrate:latest— verifywebauthn_credentialtable is createdCloses #3363
🤖 Generated with Claude Code