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

# Authorizing actions with granular MFA

> Pass an MFA token in user_auth to authorize wallet actions that require MFA from your server.

To authorize an action that requires MFA, your app's server verifies the user's MFA, then passes the MFA token with the user's JWT in `user_auth`. The SDK exchanges them for a user key that records the MFA, and signs the request with it.

The user must be enrolled in MFA first. See [enrollment with server-side MFA](https://docs.privy.io/authentication/user-authentication/mfa/server-side.md#enrollment).

## Pass the MFA token in user\_auth

Send the user an SMS code, then verify it to get an MFA token. TOTP and passkeys work the same way. See [server-side MFA](https://docs.privy.io/authentication/user-authentication/mfa/server-side.md#verification).

**NodeJS**

This requires `@privy-io/node` version `0.39.0` or later.

```ts {skip-check} theme={"system"}
// Sends a 6-digit code to the user's enrolled phone number
await privy.users().mfa.sms.verification.init(userId, {user_jwt: userJwt});

// After the user receives the code
const {token: mfaToken} = await privy.users().mfa.sms.verification.verify(userId, {
  code: '123456',
  user_jwt: userJwt
});

// Pass this as the authorization_context of any wallet request
const authorizationContext = {
  user_auth: [{user_jwt: userJwt, mfa_token: mfaToken}]
};
```

**Java**

This requires `privy-java` version `0.61.0` or later. The MFA methods are on `PrivyApiClient`.

```java theme={"system"}
PrivyApiClient privyApiClient = PrivyApiClient.builder()
    .privyAppId("insert-app-id")
    .privyAppSecret("insert-app-secret")
    .build();

// Sends a 6-digit code to the user's enrolled phone number
privyApiClient.users().mfa().sms().verification().init(userId, userJwt);

// After the user receives the code
String mfaToken = privyApiClient.users().mfa().sms().verification()
    .verify(userId, userJwt, SmsMfaVerifyRequestBody.builder().code("123456").build())
    .mfaVerificationResponse()
    .orElseThrow()
    .token();

// Pass this to any wallet method
AuthorizationContext authorizationContext = AuthorizationContext.builder()
    .addUserAuth(userJwt, mfaToken)
    .build();
```

The MFA token lasts for the cache duration. Pass the latest token on every request that may require MFA.

## Examples

Each example shows a policy that requires MFA for the action, then the request with the `authorizationContext` from above.

### Export a wallet

Exporting a private key or seed phrase requires MFA. This is the policy from [setup](https://docs.privy.io/authentication/user-authentication/mfa/granular/setup.md#create-a-policy).

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Require MFA for wallet 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 everything else",
            "method": "*",
            "conditions": [],
            "action": "ALLOW"
        }
    ]
}
```

The SDK decrypts the exported key for you.

**NodeJS**

```ts {skip-check} theme={"system"}
const {private_key} = await privy.wallets().exportPrivateKey(walletId, {
  authorization_context: authorizationContext
});
```

**Java**

```java theme={"system"}
String privateKey = privyClient.wallets().export(walletId, authorizationContext);
```

### Sign a message

Signing any message requires MFA.

```ts {skip-check} theme={"system"}
{
    "version": "1.0",
    "name": "Require MFA for message signing",
    "chain_type": "ethereum",
    "rules": [
        {
            "name": "Require MFA to sign messages",
            "method": "personal_sign",
            "conditions": [],
            "action": "ALLOW_WITH_MFA"
        },
        {
            "name": "Allow everything else",
            "method": "*",
            "conditions": [],
            "action": "ALLOW"
        }
    ]
}
```

**NodeJS**

```ts {skip-check} theme={"system"}
const {signature} = await privy.wallets().ethereum().signMessage(walletId, {
  message: 'Hello, world!',
  authorization_context: authorizationContext
});
```

**Java**

```java theme={"system"}
EthereumPersonalSignRpcResponseData response = privyClient
    .wallets()
    .ethereum()
    .signMessage(walletId, "Hello, world!", authorizationContext);
```

### Send a transaction

Sending more than 1 ETH requires MFA.

```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"
        }
    ]
}
```

**NodeJS**

```ts {skip-check} theme={"system"}
const {hash} = await privy
  .wallets()
  .ethereum()
  .sendTransaction(walletId, {
    caip2: 'eip155:8453',
    params: {
      transaction: {
        to: '0xRecipientAddress',
        value: '0x1BC16D674EC80000', // 2 ETH in wei
        chain_id: 8453
      }
    },
    authorization_context: authorizationContext
  });
```

**Java**

```java theme={"system"}
UnsignedStandardEthereumTransaction transaction = UnsignedStandardEthereumTransaction.builder()
    .to("0xRecipientAddress")
    .value(Quantity.of("0x1BC16D674EC80000")) // 2 ETH in wei
    .chainId(Quantity.of(8453))
    .build();

EthereumSendTransactionRpcResponseData response = privyClient
    .wallets()
    .ethereum()
    .sendTransaction(walletId, "eip155:8453", transaction, authorizationContext);
```

### Update a wallet

Changing a user-owned wallet's policies or signers requires the user's MFA, with or without a policy. See [resource updates](https://docs.privy.io/authentication/user-authentication/mfa/granular/when-mfa-is-required.md#resource-updates).

**NodeJS**

```ts {skip-check} theme={"system"}
const wallet = await privy.wallets().update(walletId, {
  policy_ids: [policyId],
  authorization_context: authorizationContext
});
```

**Java**

```java theme={"system"}
WalletUpdateResponse response = privyClient.wallets().update(
    walletId,
    WalletUpdateRequestBody.builder().policyIds(List.of(policyId)).build(),
    authorizationContext);
```
