RFC: auth-clerk-migration (Accepted 2026-07-02)
Seven PRs. 1 is the foundation; 2–5 are independent flows once 1 lands (2 before 3 keeps the dual-stack story testable end to end); 6 can start after 1 and must land before cutover; 7 is post-cutover cleanup.
Add the Backend API client the clerk/mod.rs comment
anticipates: create user (incl. password_digest +
password_hasher: "argon2id" import form), delete user, mark
email verified. Un-dead-code ClerkConfig and plumb it
through state.rs. No behavior change; everything stays
gated on config presence.
Login branches on clerk_id.is_some(): Clerk users verify
via FAPI create_sign_in →
attempt_second_factor; on success mint the AX session token
exactly as today (RFC §2a). Users without clerk_id keep the
homerolled path. Covers the fail-closed Clerk-outage posture and the
CLAUDE.md lifecycle test matrix (disconnect/restart/revocation; API keys
unaffected).
Re-wire signup (public_routes.rs:182-280): invite gate
unchanged → Backend API create-user → identity-only AX provisioning with
clerk_id set. Clerk email verification becomes required for
new signups, proxied through the BFF. Compensation on partial failure
(invite decrement + Clerk user delete). GUI signup/verification screens
included.
Wire the existing FAPI
create_totp/verify_totp/delete_totp
into new auth routes; expose backup codes and SMS. GUI
enrollment/challenge screens. Login challenge path from PR 2 lights up
for enrolled users.
Change-password and reset-password proxy Clerk's flows for
clerk_id users (FAPI change_password exists
already); homerolled password_reset_codes path remains only
for pre-import stragglers. GUI included.
admin-cli command: import all users' Argon2id digests verbatim via
Backend API, mark emails verified, write back clerk_id.
Lazy fallback on homerolled login (create Clerk user from just-verified
plaintext, set clerk_id). Backstop sweep for
Clerk-created-but-AX-failed rows (same pattern as the lazy- provisioning
reconciler).
After sandbox → demo → prod are fully on Clerk: delete
totp_manager.rs, the
setup_2fa/confirm_2fa/disable_2fa
routes and verify_2fa_if_enabled; drop
enabled_2fa/totp_cipher/nonce and
password_reset_codes from db/postgres/1.sql;
drop the homerolled login branch and, finally,
hashed_password.
CLERK_* env config; enable
per environment.clerk_id IS NULL logins observed for a comfortable
window.