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

# Granular MFA

> Require MFA only for the wallet actions that matter, like wallet exports and large transfers.

Granular MFA lets your app choose which wallet actions require MFA. For example, your app can require MFA when a user exports a wallet or sends a large transfer, and let routine signing go through without a prompt.

With standard transaction MFA, every wallet action requires MFA from enrolled users. With granular MFA, your app's [policies](https://docs.privy.io/controls/policies/overview.md) decide which actions require MFA. Privy evaluates those policies in its secure enclave each time a user authorizes an action.

Granular MFA works with Privy authentication and [JWT-based authentication](https://docs.privy.io/authentication/user-authentication/jwt-based-auth/overview.md), in both client-side and server-side integrations.

## Use cases

* **Wallet export.** Require MFA before a user exports a private key or seed phrase.
* **Large transfers.** Require MFA above a set amount, and let small transfers through without it.
* **Sensitive messages.** Require MFA to sign specific messages, such as an ownership proof.
* **Every action.** Require MFA for every wallet action, and require every user to enroll.

See [granular MFA policies](https://docs.privy.io/controls/policies/example-policies/granular-mfa.md) for example policies.

## Benefits

* Users complete MFA only for the actions your app's policies select. Other actions don't prompt for MFA.
* Privy enforces MFA alongside the user's authentication credentials. Actions that require MFA need both a valid user session and a completed MFA verification.
* Client-side SDKs, like the React SDK, prompt users to enroll or verify, then retry the request. Server SDKs accept the MFA token in the authorization context.

## How it works

Policy rules require MFA with the `ALLOW_WITH_MFA` action. Each rule takes one of three actions:

| Action | Result |
| - | - |
| `DENY` | Privy denies the request. |
| `ALLOW_WITH_MFA` | Privy allows the request only if the user has completed MFA. |
| `ALLOW` | Privy allows the request. |

When more than one rule matches a request, the action highest in this table applies. Privy denies a request that matches no rule.

**1. The user authorizes a wallet action**

The user signs the request with their [user
key](https://docs.privy.io/controls/authorization-keys/keys/create/user/request.md).

**2. Privy evaluates the wallet's policies**

Inside the enclave, Privy checks the request against every matching rule, and applies the action
that ranks highest in the table above.

**3. Privy checks for MFA**

If an `ALLOW_WITH_MFA` rule matches, Privy allows the request only if the user key records an
unexpired MFA verification. Otherwise, it returns [`mfa_required` or
`missing_mfa_enrollment`](https://docs.privy.io/authentication/user-authentication/mfa/granular/errors).

**4. The user completes MFA, and the request is retried**

The user enrolls or verifies, and the request is retried with a user key that records the MFA.
The React SDK does this automatically. With a server SDK, see [authorizing
actions](https://docs.privy.io/authentication/user-authentication/mfa/granular/authorizing-actions.md).

A completed verification lasts for your app's MFA cache duration, 15 minutes by default. Inside that window, the user can take more actions that require MFA without verifying again. After it, their user key still authorizes everything that doesn't require MFA.

> [!WARNING]
>
> MFA applies only to users. An [authorization key](https://docs.privy.io/controls/authorization-keys/keys/create/key.md)
> satisfies an `ALLOW_WITH_MFA` rule without MFA. If an authorization key can authorize a request on
> its own, such as a key added as a signer, the rule adds no MFA requirement to that request.
