Skip to content

MFA — what and why

Multi-factor authentication asks for proof from more than one of three categories:

  • Something you know — password, PIN.
  • Something you have — phone (for SMS), authenticator app, hardware security key.
  • Something you are — biometric (fingerprint, face).

A password alone proves only "you know the password" — phishable, leakable, reusable across services. A password plus a TOTP code from your phone adds "you have the device the TOTP secret lives on" — phishable still, but you'd need to compromise both.

WebAuthn / passkeys raise the bar further by being phishing-resistant: the credential binds to the domain it was registered against. A passkey for cymmetri.com won't sign for cymmetri-spoofed.com even if the user tries. The "something you have" category gets phishing resistance without needing extra hardware.

IntelliAuth supports five MFA factor types:

  • TOTP (Time-based One-Time Password) — authenticator apps like Google Authenticator, Authy, 1Password. The user scans a QR code at enrolment; the app generates a 6-digit code that rotates every 30 seconds. Cheap, ubiquitous, phishable.
  • WebAuthn / passkeys — hardware security keys (YubiKey, Titan) or platform authenticators (Touch ID, Windows Hello, Android biometric). Phishing-resistant. The strongest factor.
  • SMS — code sent via SMS. Familiar to users, low-friction, but phishable + SIM-swappable. Use as a fallback, not the primary.
  • Email OTP — code sent via email. Treat as roughly equivalent to SMS in security, sometimes better (depends on the user's email security).
  • Recovery codes — single-use codes generated at MFA setup. The user prints them or saves them somewhere safe. Only used when they've lost their primary MFA factor.

Two distinct moments:

  • Enrolment is when the user sets up a factor — scanning the TOTP QR code, registering the security key, etc. Happens once per factor.
  • Challenge is when the platform asks the user to prove a factor — typing the TOTP code at sign-in, tapping the security key. Happens at every flow that requires MFA.

The SDK has separate methods for each. useIntelliAuth().startMfaEnrolment(factor) starts an enrolment; the redirect-based sign-in flow handles challenges transparently (the user sees an MFA challenge page if needed; the SDK returns control once they complete it).

Three driving factors decide whether a user sees an MFA challenge on a given sign-in:

  • Tenant policy. Authentication policy might require MFA always, optional, or never.
  • AAL requirements. The action requested needs AAL2 or AAL3; the session has only AAL1; challenge to step up.
  • Risk signals. The platform's risk engine flags this sign-in attempt as unusual; ask for MFA even on an MFA-optional policy.

From your code's perspective, MFA challenges are mostly transparent — they happen inside the redirect-based sign-in flow. The SDK returns control once the user completes the challenge (or aborts).

The case you do handle is step-up: the user is already signed in, but tries to do something high-value, and the API returns mfa_required. Your code calls the SDK's step-up method to trigger a challenge without a full re-sign-in. See Step-up authentication.

You can read a user's MFA state from the SDK:

const { listMfaEnrollments } = useIntelliAuth()
const enrolments = await listMfaEnrollments()
// → [{ id, factor: 'totp', name: 'iPhone Authenticator', enrolledAt }, ...]

Useful for showing a settings UI ("you have these factors enrolled, want to add another?"). The platform manages the enrolments; your code just reads + reflects them.

A correction worth stating: MFA isn't a silver bullet. It defeats password-only attacks and most phishing; it doesn't defeat:

  • Session token theft — if an attacker steals the session cookie or refresh token after sign-in, they're past the MFA check. Defend with HttpOnly cookies, short token TTL, denylist on suspicious sessions.
  • Malware on the user's device — if their machine is compromised, any factor they enter is at risk.
  • Social engineering — convincing the user to approve a push notification they didn't initiate. Push-based MFA is the most vulnerable to this; phishing-resistant factors (passkeys) defeat it.

MFA is one layer; it's necessary but not sufficient. Treat it as part of defence in depth.