# 

## Prerequisites

* A valid API key — see [Obtain API credentials](/docs/how-tos/setup/obtain-credentials)
* An active user account with a verified email address


## How MFA works

Paradise Gateway uses **email-based OTP** for multi-factor authentication. When MFA is enabled for a user, the login flow adds a verification step.

```mermaid
---
config:
  theme: 'neutral'
---
sequenceDiagram
    participant User
    participant App as Your App
    participant API as Paradise Gateway
    User->>App: Enter username + password
    App->>API: Authenticate (Basic Auth)
    API-->>App: Requires MFA
    App->>API: POST /v1/mfa/challenges (type: 2fa)
    API-->>User: OTP sent via email
    User->>App: Enter OTP
    App->>API: POST /v1/mfa/verifications
    API-->>App: 200 OK — session authenticated
```

## Step 1: Request an OTP challenge

Send `POST /v1/mfa/challenges` with `type: 2fa` and the user's email.

curl
```shell
curl -X POST \
  https://api.dev.paradisegateway.net/v1/mfa/challenges \
  -H "Content-Type: application/json" \
  -d '{
    "user_name": "john.doe@example.com",
    "type": "2fa"
  }'
```

Python
```python
import requests

resp = requests.post(
    "https://api.dev.paradisegateway.net/v1/mfa/challenges",
    headers={"Content-Type": "application/json"},
    json={
        "user_name": "john.doe@example.com",
        "type": "2fa",
    },
)
print(resp.json())
```

JavaScript
```javascript
const resp = await fetch(
  "https://api.dev.paradisegateway.net/v1/mfa/challenges",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      user_name: "john.doe@example.com",
      type: "2fa",
    }),
  }
);
console.log(await resp.json());
```

C#
```csharp
using var client = new HttpClient();
var payload = new
{
    user_name = "john.doe@example.com",
    type = "2fa"
};
var resp = await client.PostAsJsonAsync(
    "https://api.dev.paradisegateway.net/v1/mfa/challenges", payload);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
```

Java
```java
HttpClient client = HttpClient.newHttpClient();
String body = """
    {
      "user_name": "john.doe@example.com",
      "type": "2fa"
    }
    """;
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.dev.paradisegateway.net/v1/mfa/challenges"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body)).build();
HttpResponse<String> resp = client.send(request,
    HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());
```

Go
```go
payload := strings.NewReader(`{
  "user_name": "john.doe@example.com",
  "type": "2fa"
}`)
req, _ := http.NewRequest("POST",
    "https://api.dev.paradisegateway.net/v1/mfa/challenges", payload)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
```

PHP
```php
$ch = curl_init("https://api.dev.paradisegateway.net/v1/mfa/challenges");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode([
        "user_name" => "john.doe@example.com",
        "type" => "2fa",
    ]),
]);
echo curl_exec($ch); curl_close($ch);
```

Ruby
```ruby
require "net/http"; require "json"
uri = URI("https://api.dev.paradisegateway.net/v1/mfa/challenges")
req = Net::HTTP::Post.new(uri, { "Content-Type" => "application/json" })
req.body = { user_name: "john.doe@example.com", type: "2fa" }.to_json
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts resp.body
```

### Response

A `200` response confirms the OTP was sent.

```json
{
  "status": true,
  "correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

The user receives a 6-digit OTP at their registered email address.

## Step 2: Verify the OTP

Send `POST /v1/mfa/verifications` with the OTP the user received.

curl
```shell
curl -X POST \
  https://api.dev.paradisegateway.net/v1/mfa/verifications \
  -H "Content-Type: application/json" \
  -d '{
    "user_name": "john.doe@example.com",
    "one_time_password": "123456",
    "user_interface": "api"
  }'
```

Python
```python
resp = requests.post(
    "https://api.dev.paradisegateway.net/v1/mfa/verifications",
    headers={"Content-Type": "application/json"},
    json={
        "user_name": "john.doe@example.com",
        "one_time_password": "123456",
        "user_interface": "api",
    },
)
print(resp.json())
```

JavaScript
```javascript
const resp = await fetch(
  "https://api.dev.paradisegateway.net/v1/mfa/verifications",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      user_name: "john.doe@example.com",
      one_time_password: "123456",
      user_interface: "api",
    }),
  }
);
console.log(await resp.json());
```

C#
```csharp
var verifyPayload = new
{
    user_name = "john.doe@example.com",
    one_time_password = "123456",
    user_interface = "api"
};
var resp = await client.PostAsJsonAsync(
    "https://api.dev.paradisegateway.net/v1/mfa/verifications", verifyPayload);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
```

Java
```java
String verifyBody = """
    {
      "user_name": "john.doe@example.com",
      "one_time_password": "123456",
      "user_interface": "api"
    }
    """;
HttpRequest verifyReq = HttpRequest.newBuilder()
    .uri(URI.create(
        "https://api.dev.paradisegateway.net/v1/mfa/verifications"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(verifyBody)).build();
HttpResponse<String> verifyResp = client.send(verifyReq,
    HttpResponse.BodyHandlers.ofString());
System.out.println(verifyResp.body());
```

Go
```go
payload := strings.NewReader(`{
  "user_name": "john.doe@example.com",
  "one_time_password": "123456",
  "user_interface": "api"
}`)
req, _ := http.NewRequest("POST",
    "https://api.dev.paradisegateway.net/v1/mfa/verifications", payload)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
```

PHP
```php
$ch = curl_init("https://api.dev.paradisegateway.net/v1/mfa/verifications");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode([
        "user_name" => "john.doe@example.com",
        "one_time_password" => "123456",
        "user_interface" => "api",
    ]),
]);
echo curl_exec($ch); curl_close($ch);
```

Ruby
```ruby
uri = URI("https://api.dev.paradisegateway.net/v1/mfa/verifications")
req = Net::HTTP::Post.new(uri, { "Content-Type" => "application/json" })
req.body = {
  user_name: "john.doe@example.com",
  one_time_password: "123456", user_interface: "api",
}.to_json
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts resp.body
```

### Response example

A `200` response confirms verification and returns the authenticated user and their associated clients.

```json
{
  "status": true,
  "is_client": true,
  "user": {
    "id": "USER-3MtwBwLkdIwHu7ix28a3tqPa",
    "object": "user",
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com",
    "is_mfa_active": true,
    "is_active": true,
    "is_locked": false
  },
  "clients": [
    {
      "id": "CLIENT-3MtwBwLkdIwHu7ix28a3tqPa",
      "name": "Acme Jewelry",
      "email": "john.doe@example.com",
      "role_id": 1
    }
  ],
  "correlation_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}
```

## Checking MFA status

Retrieve a user to check whether MFA is enabled.

```shell
curl https://api.dev.paradisegateway.net/v1/users/USER-3MtwBwLkdIwHu7ix28a3tqPa \
  -H "APIKEY: YOUR-API-KEY"
```

Look for `"is_mfa_active": true` in the response.

## OTP challenge types

| Type | Purpose |
|  --- | --- |
| `2fa` | Multi-factor authentication during login |
| `forgot_password` | Password reset — see [Reset a user password](/docs/how-tos/clients-users/reset-password) |


## Next steps

* [Reset a user password](/docs/how-tos/clients-users/reset-password)
* [About users and roles](/docs/concepts/account-hierarchy/users-and-roles)
* [About data protection](/docs/concepts/security-compliance/data-protection)