> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praxis-ai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Factor Authentication (MFA)

> Pria supports opt-in email-based two-step verification. This page covers how MFA works, how to manage it for users in your instance, and how to help locked-out users recover.

Email MFA adds a second factor — a 6-digit code emailed at login — between identity verification (password or OAuth) and full session access. MFA is currently **opt-in**, with a post-login nudge for admin accounts and a self-serve toggle in every user's profile.

<Info>
  MFA is enabled at the platform level. If the two-step verification options described here don't appear in your deployment, contact the Praxis AI team at [humans@praxis-ai.com](mailto:humans@praxis-ai.com).
</Info>

***

## How it works

```
User sign-in (password / OAuth)
        │
        ▼
   Identity verified
        │
        ▼
  Trusted-device cookie + matching network?
        │
        ├── yes → straight into the app
        │
        └── no  → email a 6-digit code (5 min validity)
                      │
                      ▼
              User submits code
                      │
                      ├── correct  → trusted-device cookie set,
                      │             JWT issued, into the app
                      ├── wrong    → up to 5 tries per code
                      └── 5 burned codes / hour → 10 min account lock
```

**Key choices:**

* 6-digit numeric code (matches every other auth code users see — banks, Google, etc.)
* Codes are valid for 5 minutes
* Trusted devices skip the code for 7 days by default (the window is configurable between 1 and 30 days at the platform level)
* A device only counts as trusted when **both** its trust cookie **and** its network neighbourhood match the original verification
* LTI, SDK, and API-key sign-ins are exempt — they have their own identity proofs

***

## Managing MFA per user

From **Admin → Users**, click a user's row to open the editor, then switch to the **Credentials** tab.

The **Two-step verification** block exposes:

* **Require email MFA for this user** — toggle that enables / disables MFA on the target account
* **Enabled `<date>`** caption when MFA is on
* **Revoke trusted devices** — invalidates every browser the user has previously verified. The next login from any of those browsers will re-prompt for a code

<Warning>
  **Disabling does NOT log the user out.** Per the grandfather rule, the user's current JWT keeps working until natural expiry (\~6 hours). Disable only revokes the trusted-device cookies, so the user's *next* fresh sign-in proceeds without MFA. If you need to force an immediate logout, use the [Sessions](/mdx/admin-guide/sessions) panel separately.
</Warning>

### When to disable a user's MFA

* The user is locked out (lost email access, new email address pending, mailbox bounced)
* A security incident requires temporarily reducing friction so the user can recover their account
* A standard user opted in voluntarily and changed their mind

### When to revoke trusted devices

* The user reported a device lost or stolen
* A shared / public computer was used during a recent session
* After a suspected password compromise (combined with a password reset)

***

## Audit trail

Every MFA event is recorded in an audit trail: enabling or disabling MFA (whether the user did it themselves or an admin did it for them), each code issued, resent, verified, or failed, lockouts, trusted devices added or revoked, and dismissals of the opt-in prompt. If you need an audit extract for a security review, contact the Praxis AI team at [humans@praxis-ai.com](mailto:humans@praxis-ai.com).

***

## Brute-force protection

Two layers, both enforced server-side:

1. **Per-code: 5 attempts.** The 6th wrong submission invalidates the challenge — the user must click **Resend** for a fresh code. Resends are cooldown-limited to once per 30 seconds.
2. **Per-account: 5 burned codes in 1 hour.** "Burned" means *invalidated and had at least one wrong attempt* (so a user spam-clicking Resend without submitting wrong codes doesn't trip the lock). After 5 burns the account is locked out of MFA verification for 10 minutes. If the user can't wait, an admin can disable MFA for them from the Credentials tab.

***

## Exempt paths

The MFA gate is bypassed for sign-ins that already have a strong alternate proof of identity:

* **LTI launches** — the LMS (Canvas / Moodle / Brightspace) has already authenticated the user
* **SDK launches** — the embedding application's launch token proves identity
* **API-key sign-ins** — the API key itself is a strong credential, intended for server-to-server use

Standard password and OAuth (Google / GitHub / Facebook / generic OAuth2 SSO) flows all go through the MFA gate when the user has it enabled.

***

## Frequently asked questions

<AccordionGroup>
  <Accordion title="Can I require MFA for every user in my instance?" icon="lock">
    Not in this phase. MFA is opt-in per user. The post-login nudge encourages admin accounts to enable it (with a 7-day "Remind me later" cooldown), but enforcement is the user's choice. Mandatory MFA is on the roadmap.
  </Accordion>

  <Accordion title="What happens to a user's existing session when I toggle their MFA off?" icon="clock-rotate-left">
    Their current JWT stays valid until natural expiry (default 6 hours). Their trusted-device cookies are revoked. So the user keeps working through their current session, then re-signs in normally without MFA. If you need to force an immediate logout, terminate their session from the Sessions panel separately.
  </Accordion>

  <Accordion title="Does MFA work on mobile?" icon="mobile">
    Yes. The verify form uses the `autocomplete="one-time-code"` attribute, so iOS Safari surfaces the 6-digit code from the Mail app directly in the keyboard suggestion bar — one tap to fill all six boxes. Android Chrome and Firefox have equivalent suggestions when SMS Retriever-style attributes are honored.
  </Accordion>

  <Accordion title="Backup codes?" icon="key">
    Not in this phase. Recovery is admin-assisted (another admin can disable MFA on the locked-out account; if no other admin is available, contact the Praxis AI team at [humans@praxis-ai.com](mailto:humans@praxis-ai.com)). Backup codes are tracked for a follow-up release.
  </Accordion>

  <Accordion title="What about TOTP / Authenticator apps?" icon="microchip">
    Not in this phase either. The current implementation is email-only, which keeps the recovery surface simple (admin override) and avoids managing a TOTP secret store. TOTP and WebAuthn / passkeys are on the longer-term roadmap.
  </Accordion>
</AccordionGroup>
