[PR #5267] Add WebAuthn/Passkey authentication support #4134

Open
opened 2026-02-26 08:33:16 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/NginxProxyManager/nginx-proxy-manager/pull/5267
Author: @florida117
Created: 2/3/2026
Status: 🔄 Open

Base: developHead: feature/passkey-webauthn-support


📝 Commits (10+)

  • 8d5ecf8 Add WebAuthn/Passkey authentication support
  • 680a597 Trigger CI re-run
  • 72d2cbe Fix securityScheme name in passkey OpenAPI schemas
  • fd3e5f1 Fix passkey OpenAPI schema linting warnings
  • b7b5a95 Add passkey option to setup page and fix WebAuthn origin detection
  • 9cd04b1 Add hasPassword support for passkey-only accounts
  • 467abce Add ability to remove password for passkey-enabled accounts
  • 01b4da8 Fix has_password not returned for passkey-only accounts
  • 7c1e0e8 Fix User schema to allow token/expires in setup response and add missing example
  • 32bdb12 Retrigger CI build

📊 Changes

35 files changed (+2968 additions, -730 deletions)

View changed files

📝 backend/internal/token.js (+34 -0)
📝 backend/internal/user.js (+92 -15)
backend/internal/webauthn.js (+397 -0)
backend/migrations/20260203120000_webauthn_credentials.js (+51 -0)
backend/models/webauthn_credential.js (+67 -0)
📝 backend/package.json (+1 -0)
📝 backend/routes/tokens.js (+46 -0)
📝 backend/routes/users.js (+172 -1)
📝 backend/schema/components/user-object.json (+15 -0)
backend/schema/paths/tokens/passkey/options/post.json (+64 -0)
backend/schema/paths/tokens/passkey/verify/post.json (+43 -0)
backend/schema/paths/users/userID/auth/delete.json (+67 -0)
backend/schema/paths/users/userID/passkeys/get.json (+55 -0)
backend/schema/paths/users/userID/passkeys/passkeyID/delete.json (+45 -0)
backend/schema/paths/users/userID/passkeys/passkeyID/put.json (+79 -0)
backend/schema/paths/users/userID/passkeys/register/options/post.json (+60 -0)
backend/schema/paths/users/userID/passkeys/register/verify/post.json (+81 -0)
📝 backend/schema/swagger.json (+35 -0)
📝 backend/yarn.lock (+617 -423)
📝 frontend/package.json (+1 -0)

...and 15 more files

📄 Description

Summary

Implements passwordless passkey (WebAuthn) authentication for the admin interface, as requested in #3363.

  • Passwordless login: "Sign in with Passkey" button on the login page — the browser presents registered passkeys directly, no email or password needed
  • Passkey management: Users can register, rename, and delete passkeys from the user dropdown menu
  • Bypasses 2FA: Passkeys are inherently multi-factor (device possession + biometric/PIN), so TOTP is skipped when authenticating with a passkey
  • Auto-detected RP settings: WebAuthn relying party ID and origin are automatically derived from the incoming request headers, so passkeys work behind reverse proxies without configuration

Configuration

WebAuthn relying party settings are auto-detected from the request. When behind a reverse proxy, the standard X-Forwarded-Host and X-Forwarded-Proto headers are used (Express trust proxy is already enabled).

Environment variables are available as optional overrides if needed:

Variable Default Description
WEBAUTHN_RP_ID req.hostname (auto-detected) Relying party domain
WEBAUTHN_RP_NAME Nginx Proxy Manager Display name shown during registration
WEBAUTHN_ORIGIN req.protocol + req.get("host") (auto-detected) Full origin URL

Passkeys registered under one origin will not work from a different origin — this is by design in the WebAuthn spec.

Backend changes

  • New webauthn_credential database table via Knex migration
  • New Objection.js model (backend/models/webauthn_credential.js)
  • Business logic module (backend/internal/webauthn.js) handling registration, authentication, listing, renaming, and deletion
  • getTokenFromPasskey() added to backend/internal/token.js
  • New unauthenticated routes: POST /tokens/passkey/options, POST /tokens/passkey/verify
  • New authenticated routes: GET/POST/PUT/DELETE /users/:id/passkeys/...
  • OpenAPI schema definitions for all new endpoints

Frontend changes

  • @simplewebauthn/browser integration in AuthContext (loginWithPasskey)
  • "Sign in with Passkey" button on login page (conditionally hidden if browser lacks WebAuthn support)
  • PasskeyModal for managing passkeys (register, list, rename, delete)
  • Passkeys menu item added to user dropdown in SiteHeader
  • Full i18n support via react-intl locale strings

Dependencies added

  • Backend: @simplewebauthn/server@^11.0.0
  • Frontend: @simplewebauthn/browser@^11.0.0

Test plan

  • Run npx knex migrate:latest — verify webauthn_credential table is created
  • Verify existing email/password login still works
  • Verify existing TOTP 2FA flow still works
  • Register a passkey via Settings > Passkeys
  • Log out and sign in using the "Sign in with Passkey" button (passwordless)
  • Rename a registered passkey
  • Delete a registered passkey
  • Verify passkey button is hidden in browsers without WebAuthn support
  • Test with different databases (SQLite, MySQL, PostgreSQL)
  • Test access via reverse proxy — RP settings should auto-detect from forwarded headers

Closes #3363

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/NginxProxyManager/nginx-proxy-manager/pull/5267 **Author:** [@florida117](https://github.com/florida117) **Created:** 2/3/2026 **Status:** 🔄 Open **Base:** `develop` ← **Head:** `feature/passkey-webauthn-support` --- ### 📝 Commits (10+) - [`8d5ecf8`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/8d5ecf8f68ea2c0bf31149a9832248334a6f5446) Add WebAuthn/Passkey authentication support - [`680a597`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/680a5979889d63b8c7fbdd68f60df37f9263b031) Trigger CI re-run - [`72d2cbe`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/72d2cbeae5098fcb3110f2bdf51bf31896339fb8) Fix securityScheme name in passkey OpenAPI schemas - [`fd3e5f1`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/fd3e5f1a5ae48f1c1b362fc3da4e607f13c77580) Fix passkey OpenAPI schema linting warnings - [`b7b5a95`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/b7b5a95488dcd6d7966d992f42cf9c53740eaa45) Add passkey option to setup page and fix WebAuthn origin detection - [`9cd04b1`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/9cd04b166b07e595433e7f04dbdf652fb23b1361) Add hasPassword support for passkey-only accounts - [`467abce`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/467abce57a26f76799411a57465067d30c155246) Add ability to remove password for passkey-enabled accounts - [`01b4da8`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/01b4da8504bbb6cb0d8323fdff8206db3ad2ce1a) Fix has_password not returned for passkey-only accounts - [`7c1e0e8`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/7c1e0e8b8c8f7e465fea32c627943b06e6a01cd8) Fix User schema to allow token/expires in setup response and add missing example - [`32bdb12`](https://github.com/NginxProxyManager/nginx-proxy-manager/commit/32bdb12189ed1bb7e46176a7a4013f3d0966c548) Retrigger CI build ### 📊 Changes **35 files changed** (+2968 additions, -730 deletions) <details> <summary>View changed files</summary> 📝 `backend/internal/token.js` (+34 -0) 📝 `backend/internal/user.js` (+92 -15) ➕ `backend/internal/webauthn.js` (+397 -0) ➕ `backend/migrations/20260203120000_webauthn_credentials.js` (+51 -0) ➕ `backend/models/webauthn_credential.js` (+67 -0) 📝 `backend/package.json` (+1 -0) 📝 `backend/routes/tokens.js` (+46 -0) 📝 `backend/routes/users.js` (+172 -1) 📝 `backend/schema/components/user-object.json` (+15 -0) ➕ `backend/schema/paths/tokens/passkey/options/post.json` (+64 -0) ➕ `backend/schema/paths/tokens/passkey/verify/post.json` (+43 -0) ➕ `backend/schema/paths/users/userID/auth/delete.json` (+67 -0) ➕ `backend/schema/paths/users/userID/passkeys/get.json` (+55 -0) ➕ `backend/schema/paths/users/userID/passkeys/passkeyID/delete.json` (+45 -0) ➕ `backend/schema/paths/users/userID/passkeys/passkeyID/put.json` (+79 -0) ➕ `backend/schema/paths/users/userID/passkeys/register/options/post.json` (+60 -0) ➕ `backend/schema/paths/users/userID/passkeys/register/verify/post.json` (+81 -0) 📝 `backend/schema/swagger.json` (+35 -0) 📝 `backend/yarn.lock` (+617 -423) 📝 `frontend/package.json` (+1 -0) _...and 15 more files_ </details> ### 📄 Description ## Summary Implements passwordless passkey (WebAuthn) authentication for the admin interface, as requested in #3363. - **Passwordless login**: "Sign in with Passkey" button on the login page — the browser presents registered passkeys directly, no email or password needed - **Passkey management**: Users can register, rename, and delete passkeys from the user dropdown menu - **Bypasses 2FA**: Passkeys are inherently multi-factor (device possession + biometric/PIN), so TOTP is skipped when authenticating with a passkey - **Auto-detected RP settings**: WebAuthn relying party ID and origin are automatically derived from the incoming request headers, so passkeys work behind reverse proxies without configuration ### Configuration WebAuthn relying party settings are **auto-detected** from the request. When behind a reverse proxy, the standard `X-Forwarded-Host` and `X-Forwarded-Proto` headers are used (Express `trust proxy` is already enabled). Environment variables are available as optional overrides if needed: | Variable | Default | Description | |---|---|---| | `WEBAUTHN_RP_ID` | `req.hostname` (auto-detected) | Relying party domain | | `WEBAUTHN_RP_NAME` | `Nginx Proxy Manager` | Display name shown during registration | | `WEBAUTHN_ORIGIN` | `req.protocol + req.get("host")` (auto-detected) | Full origin URL | Passkeys registered under one origin will not work from a different origin — this is by design in the WebAuthn spec. ### Backend changes - New `webauthn_credential` database table via Knex migration - New Objection.js model (`backend/models/webauthn_credential.js`) - Business logic module (`backend/internal/webauthn.js`) handling registration, authentication, listing, renaming, and deletion - `getTokenFromPasskey()` added to `backend/internal/token.js` - New unauthenticated routes: `POST /tokens/passkey/options`, `POST /tokens/passkey/verify` - New authenticated routes: `GET/POST/PUT/DELETE /users/:id/passkeys/...` - OpenAPI schema definitions for all new endpoints ### Frontend changes - `@simplewebauthn/browser` integration in `AuthContext` (`loginWithPasskey`) - "Sign in with Passkey" button on login page (conditionally hidden if browser lacks WebAuthn support) - `PasskeyModal` for managing passkeys (register, list, rename, delete) - Passkeys menu item added to user dropdown in `SiteHeader` - Full i18n support via `react-intl` locale strings ### Dependencies added - Backend: `@simplewebauthn/server@^11.0.0` - Frontend: `@simplewebauthn/browser@^11.0.0` ## Test plan - [ ] Run `npx knex migrate:latest` — verify `webauthn_credential` table is created - [ ] Verify existing email/password login still works - [ ] Verify existing TOTP 2FA flow still works - [ ] Register a passkey via Settings > Passkeys - [ ] Log out and sign in using the "Sign in with Passkey" button (passwordless) - [ ] Rename a registered passkey - [ ] Delete a registered passkey - [ ] Verify passkey button is hidden in browsers without WebAuthn support - [ ] Test with different databases (SQLite, MySQL, PostgreSQL) - [ ] Test access via reverse proxy — RP settings should auto-detect from forwarded headers Closes #3363 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/nginx-proxy-manager-NginxProxyManager#4134
No description provided.