Skip to content

feat(auth): add passkey support - #4957

Merged
Siumauricio merged 3 commits into
canaryfrom
feat/passkeys
Aug 3, 2026
Merged

feat(auth): add passkey support#4957
Siumauricio merged 3 commits into
canaryfrom
feat/passkeys

Conversation

@Siumauricio

@Siumauricio Siumauricio commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds WebAuthn passkeys via better-auth's @better-auth/passkey plugin (same scoped-package family as sso/scim/api-key, pinned to 1.6.23).

  • Server: passkey() plugin registered in auth.ts (mirrored in auth-cli.ts). rpID/origin are derived from the request, so it works for both IP and custom-domain panels without config.
  • DB: new passkey table (+ indexes on user_id / credential_id), migration 0177_glossy_brother_voodoo.sql.
  • Profile: new "Passkeys" dialog next to the 2FA buttons — list, register (named), and remove passkeys.
  • Login: "Sign in with Passkey" button below the credentials form; cancelled WebAuthn prompts don't surface as errors.
  • Session: freshAge: 0 — otherwise sessions older than 24h get a 403 "Session is not fresh" when managing passkeys (Dokploy doesn't use any other better-auth endpoint gated by freshness; password changes still require the current password).

Greptile Summary

Adds Better Auth passkey support across authentication, persistence, login, and profile management.

  • Registers the passkey server and client plugins.
  • Adds the passkey table, migration, schema, and listing service.
  • Adds passkey enrollment, deletion, and sign-in interfaces.

Confidence Score: 4/5

The PR does not appear safe to merge until the outstanding global session-freshness bypass is fixed.

The runtime auth configuration still sets freshAge: 0 while passkey enrollment remains directly reachable, leaving the previously reported path for an older stolen session to register a durable credential.

Files Needing Attention: packages/server/src/lib/auth.ts

Reviews (3): Last reviewed commit: "fix(build): exclude CLI-only auth config..." | Re-trigger Greptile

Context used:

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Aug 2, 2026
session: {
expiresIn: 60 * 60 * 24 * 3,
updateAge: 60 * 60 * 24,
freshAge: 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Global freshness boundary disabled

If an attacker holds an older but still-valid session, freshAge: 0 makes fresh-session middleware skip its age check, allowing that session to enroll an attacker-controlled passkey and establish durable account access; this global setting also removes freshness protection from other sensitive better-auth operations such as account unlinking.

How this was verified: Better-auth treats zero as disabling the age check, and passkey registration uses its fresh-session middleware.

Knowledge Base Used: Authentication, Organizations, and Permissions

@Siumauricio
Siumauricio merged commit 5d11a04 into canary Aug 3, 2026
5 checks passed
@Siumauricio
Siumauricio deleted the feat/passkeys branch August 3, 2026 08:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant