> ## 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 policies

> Example policies that use ALLOW_WITH_MFA rules to require MFA for specific wallet actions.

[Granular MFA](https://docs.privy.io/authentication/user-authentication/mfa/granular/overview.md) policies use the `ALLOW_WITH_MFA` rule action to require MFA for specific wallet actions. Privy allows a request that matches an `ALLOW_WITH_MFA` rule only if the user authorizing it has completed MFA. A user who hasn't enrolled in MFA must enroll first.

When more than one rule matches a request, Privy applies the most restrictive action. `DENY` takes precedence over `ALLOW_WITH_MFA`, and `ALLOW_WITH_MFA` takes precedence over `ALLOW`. Privy denies a request that matches no rule, so pair `ALLOW_WITH_MFA` rules with `ALLOW` rules for actions that don't need MFA.

## Require MFA for every wallet action

Every user must enroll in MFA before using a wallet with this policy. A wildcard `ALLOW_WITH_MFA` rule also denies actions that policies can't evaluate yet, such as swaps.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Always require MFA",
    "chain_type": "ethereum",
    "rules": [
        {
            "name": "Require MFA for every action",
            "method": "*",
            "conditions": [],
            "action": "ALLOW_WITH_MFA"
        }
    ]
}
```

## Require MFA to sign a specific message

Signing `Hello, world!` requires MFA. Every other action, including signing other messages, doesn't.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Require MFA for Hello, world!",
    "chain_type": "ethereum",
    "rules": [
        {
            "name": "Require MFA to sign Hello, world!",
            "method": "personal_sign",
            "conditions": [
                {
                    "field_source": "message",
                    "field": "content",
                    "operator": "eq",
                    "value": "Hello, world!"
                }
            ],
            "action": "ALLOW_WITH_MFA"
        },
        {
            "name": "Allow everything else",
            "method": "*",
            "conditions": [],
            "action": "ALLOW"
        }
    ]
}
```

## Require MFA for wallet export

Exporting a private key or seed phrase requires MFA. Every other action doesn't.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Require MFA for export",
    "chain_type": "ethereum",
    "rules": [
        {
            "name": "Require MFA for private key export",
            "method": "exportPrivateKey",
            "conditions": [],
            "action": "ALLOW_WITH_MFA"
        },
        {
            "name": "Require MFA for seed phrase export",
            "method": "exportSeedPhrase",
            "conditions": [],
            "action": "ALLOW_WITH_MFA"
        },
        {
            "name": "Allow other actions",
            "method": "*",
            "conditions": [],
            "action": "ALLOW"
        }
    ]
}
```

## Require MFA for large transfers

[Transfers](https://docs.privy.io/wallets/actions/transfer/policies.md) of more than 1000 OUSD require MFA. Smaller transfers don't. The transfer API accepts the amount in `amount` or `source.amount`, so the policy checks both. This policy covers only the `transfer` method, so add rules for any other methods the wallet uses.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Transfers",
    "chain_type": "ethereum",
    "rules": [
        {
            "name": "Allow transfers",
            "method": "transfer",
            "conditions": [],
            "action": "ALLOW"
        },
        {
            // A large OUSD transfer matches this rule and the ALLOW rule, and ALLOW_WITH_MFA takes precedence.
            "name": "Require MFA for OUSD over 1000 (amount)",
            "method": "transfer",
            "conditions": [
                {
                    "field_source": "action_request_body",
                    "field": "source.asset",
                    "operator": "eq",
                    "value": "ousd"
                },
                {
                    "field_source": "action_request_body",
                    "field": "amount",
                    "operator": "gt",
                    "value": "1000.0"
                }
            ],
            "action": "ALLOW_WITH_MFA"
        },
        {
            "name": "Require MFA for OUSD over 1000 (source.amount)",
            "method": "transfer",
            "conditions": [
                {
                    "field_source": "action_request_body",
                    "field": "source.asset",
                    "operator": "eq",
                    "value": "ousd"
                },
                {
                    "field_source": "action_request_body",
                    "field": "source.amount",
                    "operator": "gt",
                    "value": "1000.0"
                }
            ],
            "action": "ALLOW_WITH_MFA"
        }
    ]
}
```

## Require MFA for large ETH transfers

Sending more than 1 ETH requires MFA. Smaller transactions don't.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Require MFA for large ETH transfers",
    "chain_type": "ethereum",
    "rules": [
        {
            "name": "Require MFA for transfers over 1 ETH",
            "method": "eth_sendTransaction",
            "conditions": [
                {
                    "field_source": "ethereum_transaction",
                    "field": "value",
                    "operator": "gt",
                    "value": "0xDE0B6B3A7640000" // 1 ETH in wei
                }
            ],
            "action": "ALLOW_WITH_MFA"
        },
        {
            "name": "Allow everything else",
            "method": "*",
            "conditions": [],
            "action": "ALLOW"
        }
    ]
}
```

## Let a user own the policy

Updating or deleting a user-owned policy requires the owner's MFA. Creating it doesn't. This stops a user from removing an `ALLOW_WITH_MFA` rule without MFA.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Always require MFA",
    "chain_type": "ethereum",
    "owner": {
        "user_id": "did:privy:cm9x2k7d1000abc123"
    },
    "rules": [
        {
            "name": "Require MFA for every action",
            "method": "*",
            "conditions": [],
            "action": "ALLOW_WITH_MFA"
        }
    ]
}
```
