Proposal: adopting passkeys in frappe/frappe

Status: design proposal, not a compatibility statement. Nothing in this directory means that a current Frappe branch contains the required extension points, schema, migration, or handover contract. Before opening an upstream PR, rebase onto the then-current develop, inspect the real code, replace sketches with a tested diff, and complete VALIDATE.md.

This directory breaks possible core adoption into reviewable decisions. It intentionally avoids pinned commit hashes, line numbers, patch sizes, test counts, and claims that existing core behavior is identical. Those facts expire quickly and must be recorded in the eventual PR with an explicit date and ref.

Proposed sequence

  1. Confirm current core architecture. Map login, two-factor dispatch, System Settings, /.well-known/ routing, session minting, hooks, templates, and test infrastructure at the target ref. Treat every document here as input, not proof.
  2. Agree on schema and ownership. Core maintainers choose module placement, DocType names, System Settings fields, public API names, and whether a generic two-factor provider registry is desirable.
  3. Land the smallest prerequisite seams. The sketches cover a two-factor provider interface, password-disable validation, a native login-page slot, the webauthn autocomplete token, settings placement, and an enrollment-scoped email link. These may be changed, combined, or rejected during review.
  4. Build native passkeys from the hardened contracts. Port behavior, not files blindly. The app’s hooks and released-branch shims are not automatically suitable for core.
  5. Prove migration and handover. Test an app-installed site with real credential/settings data, then test the installed app and native core together before advertising dormancy.
  6. Release incrementally. Keep the app authoritative until a released core version has passed the migration, coexistence, rollback, and recovery gates.

Non-negotiable contract checklist

Any native proposal must preserve or deliberately supersede these app guarantees:

Proposed work packages

Package Purpose Document
Two-factor provider seam Let a reviewed provider issue and verify a non-OTP second factor without monkeypatching patch-01-two-factor-methods-registry.md
Password-disable validator Count native passkey login as a surviving method only when the native feature and fields exist patch-02-disable-user-pass-login-validator.md
Native login surface Render and initialize passkeys without DOM-injection shims patch-03-login-page-context-template.md
Conditional UI token Add webauthn to the username autocomplete contract patch-04-autocomplete-webauthn-token.md
Settings placement Decide whether passkey settings fold into System Settings or stay a linked single patch-05-system-settings-placement.md
Enrollment-scoped email links Bootstrap passkey setup on password-disabled sites via a purpose-scoped magic link patch-06-enrollment-scoped-email-link.md
App-to-core inventory Decide port/fold/replace/discard per component at PR time mapping.md
Acceptance gates Rebase, security, migration, coexistence, rollback, and release checks VALIDATE.md

Handover boundary

The marker is a promise by core, not feature detection:

FRAPPE_PASSKEYS_APP_HANDOVER = "frappe-passkeys-app-handover-v1"

Core must not define it until its implementation has passed the complete coexistence and migration suite. The app’s fresh-install guard is intentionally more conservative: any frappe.passkey module blocks a new install to prevent two fresh authorities. That guard does not make a partial module a safe automatic handover for existing sites.

Prior work

Historical Frappe issues and PRs can inform design, but their status and review conclusions must be checked live before citing them in a PR. Do not describe a closed or stale proposal as approved without a current maintainer decision.

Request hardening to preserve

Core adoption must preserve the app’s IPv6 /64 guest limits, email-only lowercase normalization, OAuth password-grant token insertion guard, and integer settings bounds documented in configuration.md.