Per-app 2FA requirement
Make 2FA a condition of sign-in for your app — appropriate for banking, health, finance, or anywhere a stolen primary credential shouldn't be enough.
Enable it on your app
On /dashboard/developer, expand your app's row and flip the Require 2FA toggle. Take effect is immediate — the next user attempting to authorize must have 2FA enrolled on their Whisp3r Auth account.
What happens at the consent screen
If a user has 2FA enabled, they pass straight through (after a fresh TOTP challenge if their session hasn't completed one). If they don't have 2FA enabled, the consent screen shows a dedicated panel:
- Your app name + icon
- "YourApp requires two-factor authentication. This app only authorizes accounts protected by 2FA. Enable two-factor authentication on your account and return to continue."
- A primary CTA linking to /dashboard/security
- A secondary "Deny" that sends the user back to your
redirect_uriwitherror=access_denied
The user enables 2FA, returns, and re-authorizes — they end up back at your app with normal tokens.
Reading 2FA status from the id_token
Even if you don't require 2FA at sign-in, your id_token carries an amr claim when the user passed 2FA on this
session:
{
"sub": "...",
"amr": ["mfa", "otp"]
} The claim is absent when the user signed in with a single factor — that's the OIDC convention for "single-factor auth happened" (per OIDC Core §2). Branch on presence, not on value:
const usedMfa = Array.isArray(idToken.amr) && idToken.amr.includes('mfa');
if (usedMfa) {
// safe to unlock the high-assurance feature
} Defense in depth
The toggle is enforced in two places:
- Consent screen — the UI refuses to authorize before the user toggles consent on.
- Server gate — the
POST /api/oauth/consentendpoint re-checks the user's 2FA status against your app's metadata. A hand-rolled curl that bypasses the UI still gets rejected.
Existing users
Users who consented to your app BEFORE you turned on the requirement keep their existing access until their refresh token expires. Their next sign-in (or any flow that re-prompts consent) enforces the requirement.
If you need to force every connected user through the gate immediately — e.g. you're tightening security in response to an incident — revoke all consents for your app via the dashboard and they'll re-consent (under the new requirement) on next sign-in.