AuthMethodsPromptDialog
src/lib/components/auth/AuthMethodsPromptDialog.svelte
Purpose
A unified persistent dialog that prompts users who lack a passkey and/or an
MFA factor to enroll one right after login. It auto-opens when the
conditions are met and runs the enrollment ceremony inline via two inner
components: PasskeyEnrollmentSection and MfaEnrollmentSection.
Replaces the previous separate PasskeyPromptDialog and MfaPromptDialog
components (per the root MFA/2FA plan, Surface B). Use it once, mounted in
the app shell or a top-level layout.
Origin
Custom — Primebrick-written. Uses the shadcn-svelte™ Dialog +
Button + Checkbox + Choicebox, @lucide/svelte icons
(Fingerprint, KeyRound, ShieldCheck, Smartphone, AlertTriangle),
and the WebAuthn codec helpers. Not vendored from any registry.
Usage
Mount once
Code
The component takes no props. It reads userProfileStore and
authConfigState and manages its own open state via an $effect.
Required passkey mode
When passkey_required is true in the auth config:
- Only the passkey method is shown (no MFA card, no method selector)
- The "don't ask again" checkbox is hidden
- The "Not now" dismiss button is hidden
- The user must enroll a passkey to proceed
Optional mode (passkey_required=false)
When passkey_required is false:
- Both passkey and MFA method cards are shown (if the user lacks them)
- The user can pick a method from the selector
- "Not now" dismiss button is visible
- "Don't show this again" checkbox is visible
- MFA is never mandatory at the dialog level — real MFA enforcement is per-route step-up (Surface C)
Behavior
- Auto-show:
shouldShowistruewhen the profile has auuidand:passkey_required=true: user has no passkeypasskey_required=false: (user needs passkey OR MFA) AND not dismissed An$effectmirrorsshouldShowintoopen.
- Persistent:
showCloseButtonisfalse, Escape is ignored, and clicking outside triggers a "bump" shake animation instead of closing. - Method selector: when
passkey_required=false, aChoiceboxshows passkey and MFA cards. Selecting one reveals the corresponding enrollment section. - Passkey enrollment inline:
POST /api/v1/auth/webauthn/signup/begin→navigator.credentials.create()→POST /api/v1/auth/webauthn/signup/finish. On success the profile store is updated withhas_passkey: trueand the dialog closes. - MFA enrollment inline (Option B):
POST /api/v1/auth/mfa/enroll/begin→ QR code displayed → user scans with OTP app →POST /api/v1/auth/mfa/enroll/finish. On success the profile store is updated withhas_mfa: trueand the dialog closes. - Already enrolled: an
InvalidStateErrortriggers a best-effortsync-passkeyscall, updates the store, and closes the dialog. - Dismiss: when "don't show this again" is checked,
POST /api/v1/auth/me/dismiss-auth-method-enforcerpersists the dismissal and the store is updated withauth_method_enforcer_dismissed: true. Without the checkbox, the dismiss is session-only (dialog reappears on next login). - Notifications: all outcomes go through
pushNotification.
Props
Full prop table: see API reference — authmethodspromptdialog.
This component takes no props.
Inner components
PasskeyEnrollmentSection—src/lib/components/auth/PasskeyEnrollmentSection.svelteMfaEnrollmentSection—src/lib/components/auth/MfaEnrollmentSection.svelte