Proposal: Authenticator app and push MFA
Passkeys stay the phishing-resistant primary (ADR-0020), TOTP the offline fallback — but the factor the incumbents deploy fleet-wide is push approve/deny in a first-party app. This adds that app — number matching mandatory, approvals signed — plus a Temporary Access Pass for bootstrap.
Motivation
Push MFA is the default workforce factor at the incumbents (Microsoft
Authenticator, Okta Verify); an MFA story that stops at TOTP loses that
comparison. ADR-0020 scoped v1
to WebAuthn and TOTP, amr/acr stamped for edge enforcement; proposal 0011
built the delivery seam push would ride. Push also has a public failure mode
(MFA-fatigue bombing) a new entrant must design out, not patch in — and
neither factor closes the onboarding gap: how does a passwordless user enroll
a first strong factor? This proposal answers all three.
Design
-
A first-party authenticator app, open-source, with push approve/deny as its core factor. The app also acts as a TOTP generator (the fallback that works offline) and composes with platform passkeys rather than replacing them — passkeys stay the phishing-resistant primary (ADR-0020); push covers what they don't: step-up prompts on a session already past login, legacy devices without authenticators, approve-from-another-device.
-
Number matching is mandatory, not optional. Every push challenge displays a code at the login surface that the user must enter or select in the app — blind tap-to-approve is refused by design, because MFA-fatigue bombing defeated it industry-wide. Deny-with-reason ("this wasn't me") is a first-class response feeding the risk pipeline (ADR-0031) and attack protection (proposal 0021) as a high-signal event.
-
Enrollment is a Modgud ceremony; the device becomes a registered authenticator. QR / deep-link enrollment runs behind the multi-step AuthenticationMethod seam at Modgud (ADR-0009). The device holds a hardware-backed keypair (keystore / secure enclave) and signs every push response — approval is a cryptographic assertion, not a button press over TLS. The device record feeds device trust (ADR-0031); an
amrvalue distinct from TOTP and WebAuthn letsacrpolicy distinguish factor strength — ADR-0020's model unchanged. -
Push transport rides the notification seam, with an honest vendor wrinkle. Delivery is a new channel type on proposal 0011's trait, targeting platform push (APNs/FCM) — which fails the ADR-0008 control-resilience test: no self-hostable path into Apple's and Google's push networks. That is named, not hidden: push is never the only path — the app polls for pending challenges on open, TOTP always works offline, and an unanswered push times out to deny within a stated budget (ADR-0039 registry entry). Self-hostable push (ntfy/UnifiedPush) is an open question, not a v1 promise.
-
A Temporary Access Pass (TAP) bootstraps enrollment. A time-boxed, limited-use credential an admin — or delegated helpdesk, proposal 0016 — issues so a new or recovering user can enroll a first strong factor without ever holding a password: the gap TOTP and WebAuthn leave open. TAP is an AuthenticationMethod (ADR-0015) with forced expiry and use-count, satisfies only enrollment-grade
acr(never step-up), and every issue and use is an event (ADR-0022). -
Everything is evented and budgeted. Challenge issued / approved / denied / timed-out, enrollments, TAP lifecycle — all on the stream (ADR-0022) for audit and risk; push round-trip latency is a freshness-plane signal (proposal 0012).
Alternatives considered
- SMS/voice as the push substitute. Rejected as flagship — SIM-swap-weak; they exist as explicit low-assurance methods per ADR-0015, never the lead.
- Third-party TOTP apps only. Already supported and stays supported — but no push, no deny signal, no number matching.
- A FIDO2-only strategy. Passkeys remain primary, but push covers real surfaces passkeys don't: step-up on an established session, legacy devices, delegated approval from another device.
- Building on a hosted MFA vendor. Fails the ADR-0008 control-resilience test as the only path — the same call proposal 0021 made for bot detection.
Graduation criteria
- Push with number matching works end-to-end against the reference app; a blind approve is impossible — asserted as an abuse-case test (ADR-0028).
- An MFA-fatigue burst (repeated pushes at one user) trips attack-protection throttling (proposal 0021) — abuse-case tested.
- Deny-with-reason lands in the risk pipeline as a scored signal.
- TAP enrolls a first factor and cannot satisfy step-up
acr. - An unanswered push times out to deny within the registered budget; a forged approval is rejected by signed-response verification.
Backward compatibility
- New
amrvalue(s) and TAP ride existing seats — ADR-0015 methods, ADR-0020 claims, proposal 0011 channels — no token-format change. - The app protocol is versioned from v0 like every external format (ADR-0003 discipline).
Open questions
- Is a self-hostable push transport (UnifiedPush/ntfy) viable alongside APNs/FCM?
- Does the authenticator app double as a passkey holder, or stay factor-only?
- TAP default lifetime and use-count.
- iOS/Android release cadence vs the server's — the app is a separately-versioned artifact.