# Email OTP & SMS OTP Authentication

Authenticate users using one-time passwords (OTP) sent via email or SMS. This provides a passwordless authentication experience where users don't need to remember a password.

For traditional password-based authentication, see [Email and Password](/docs/products/embedded-wallet/unity/auth/email).

## Email OTP methods

### RequestEmailOtp

Request a one-time password to be sent to the user's email address.

**Method Signature:**

```csharp
public async UniTask RequestEmailOtp(string email)
```

**Parameters:**

* `string email` - The email address to send the OTP to

**Returns:**

* `UniTask` - Completes when the OTP is sent

**Example:**

```csharp
using System;
using UnityEngine;
using Cysharp.Threading.Tasks;
using Openfort.OpenfortSDK;

public class EmailOtpManager : MonoBehaviour
{
    private OpenfortSDK openfort;

    private async void Start()
    {
        openfort = await OpenfortSDK.Init("YOUR_OPENFORT_PUBLISHABLE_KEY");
    }

    public async UniTask SendEmailOtp(string email)
    {
        try
        {
            await openfort.RequestEmailOtp(email);
            Debug.Log($"OTP sent to {email}");
        }
        catch (Exception e)
        {
            Debug.LogError($"Failed to send OTP: {e.Message}");
            throw;
        }
    }
}
```

### LogInWithEmailOtp

Authenticate the user with the OTP received via email.

**Method Signature:**

```csharp
public async UniTask<AuthResponse> LogInWithEmailOtp(string email, string otp)
```

**Parameters:**

* `string email` - The email address that received the OTP
* `string otp` - The one-time password entered by the user

**Returns:**

* `UniTask<AuthResponse>` - Authentication response with user info and session

**Example:**

```csharp
public async UniTask<AuthResponse> LoginWithEmailOtp(string email, string otp)
{
    try
    {
        var response = await openfort.LogInWithEmailOtp(email, otp);

        Debug.Log($"Login successful: {response.User.Id}");
        Debug.Log($"Email: {response.User.Email}");

        return response;
    }
    catch (Exception e)
    {
        Debug.LogError($"Email OTP login failed: {e.Message}");
        throw;
    }
}
```

### VerifyEmailOtp

Verify an email OTP without logging in. Useful for email verification flows.

**Method Signature:**

```csharp
public async UniTask VerifyEmailOtp(string email, string otp)
```

**Parameters:**

* `string email` - The email address to verify
* `string otp` - The one-time password

**Returns:**

* `UniTask` - Completes when verification is successful

**Example:**

```csharp
public async UniTask VerifyEmail(string email, string otp)
{
    try
    {
        await openfort.VerifyEmailOtp(email, otp);
        Debug.Log($"Email {email} verified successfully");
    }
    catch (Exception e)
    {
        Debug.LogError($"Email verification failed: {e.Message}");
        throw;
    }
}
```

## Phone OTP methods

### RequestPhoneOtp

Request a one-time password to be sent to the user's phone number via SMS.

**Method Signature:**

```csharp
public async UniTask RequestPhoneOtp(string phoneNumber)
```

**Parameters:**

* `string phoneNumber` - The phone number to send the OTP to (with country code, for example, "+1234567890")

**Returns:**

* `UniTask` - Completes when the OTP is sent

**Example:**

```csharp
public async UniTask SendPhoneOtp(string phoneNumber)
{
    try
    {
        await openfort.RequestPhoneOtp(phoneNumber);
        Debug.Log($"OTP sent to {phoneNumber}");
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to send phone OTP: {e.Message}");
        throw;
    }
}
```

### LogInWithPhoneOtp

Authenticate the user with the OTP received via SMS.

**Method Signature:**

```csharp
public async UniTask<AuthResponse> LogInWithPhoneOtp(string phoneNumber, string otp)
```

**Parameters:**

* `string phoneNumber` - The phone number that received the OTP
* `string otp` - The one-time password entered by the user

**Returns:**

* `UniTask<AuthResponse>` - Authentication response with user info and session

**Example:**

```csharp
public async UniTask<AuthResponse> LoginWithPhoneOtp(string phoneNumber, string otp)
{
    try
    {
        var response = await openfort.LogInWithPhoneOtp(phoneNumber, otp);

        Debug.Log($"Login successful: {response.User.Id}");
        Debug.Log($"Phone: {response.User.PhoneNumber}");
        Debug.Log($"Phone Verified: {response.User.PhoneNumberVerified}");

        return response;
    }
    catch (Exception e)
    {
        Debug.LogError($"Phone OTP login failed: {e.Message}");
        throw;
    }
}
```

### LinkPhoneOtp

Link a phone number to an existing authenticated user account.

**Method Signature:**

```csharp
public async UniTask<AuthResponse> LinkPhoneOtp(LinkPhoneOtpRequest request)
```

**Parameters:**

* `LinkPhoneOtpRequest request` - Request containing phone number and OTP

**Returns:**

* `UniTask<AuthResponse>` - Updated authentication response

**LinkPhoneOtpRequest Structure:**

```csharp
public class LinkPhoneOtpRequest
{
    public string phoneNumber;  // Phone number to link
    public string otp;          // One-time password received via SMS

    public LinkPhoneOtpRequest(string phoneNumber, string otp)
    {
        this.phoneNumber = phoneNumber;
        this.otp = otp;
    }
}
```

**Example:**

```csharp
public async UniTask<AuthResponse> LinkPhone(string phoneNumber, string otp)
{
    try
    {
        var request = new LinkPhoneOtpRequest(phoneNumber, otp);

        var response = await openfort.LinkPhoneOtp(request);

        Debug.Log($"Phone linked to user: {response.User.Id}");
        Debug.Log($"Phone: {response.User.PhoneNumber}");

        return response;
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to link phone: {e.Message}");
        throw;
    }
}
```

## Complete OTP authentication example

Here's a complete example implementing both email and phone OTP flows:

```csharp
using System;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using Cysharp.Threading.Tasks;
using Openfort.OpenfortSDK;
using Openfort.OpenfortSDK.Model;

public class OtpAuthUI : MonoBehaviour
{
    [Header("Email OTP UI")]
    [SerializeField] private TMP_InputField emailInput;
    [SerializeField] private TMP_InputField emailOtpInput;
    [SerializeField] private Button sendEmailOtpButton;
    [SerializeField] private Button verifyEmailOtpButton;

    [Header("Phone OTP UI")]
    [SerializeField] private TMP_InputField phoneInput;
    [SerializeField] private TMP_InputField phoneOtpInput;
    [SerializeField] private Button sendPhoneOtpButton;
    [SerializeField] private Button verifyPhoneOtpButton;

    [Header("Status")]
    [SerializeField] private TMP_Text statusText;
    [SerializeField] private GameObject otpInputPanel;

    private OpenfortSDK openfort;
    private string currentEmail;
    private string currentPhone;
    private bool isEmailFlow;

    private async void Start()
    {
        openfort = await OpenfortSDK.Init(
            "YOUR_OPENFORT_PUBLISHABLE_KEY",
            "YOUR_SHIELD_PUBLISHABLE_KEY"
        );

        // Setup button listeners
        sendEmailOtpButton.onClick.AddListener(() => HandleSendEmailOtp().Forget());
        verifyEmailOtpButton.onClick.AddListener(() => HandleVerifyEmailOtp().Forget());
        sendPhoneOtpButton.onClick.AddListener(() => HandleSendPhoneOtp().Forget());
        verifyPhoneOtpButton.onClick.AddListener(() => HandleVerifyPhoneOtp().Forget());

        otpInputPanel.SetActive(false);
    }

    private async UniTaskVoid HandleSendEmailOtp()
    {
        string email = emailInput.text;
        if (string.IsNullOrEmpty(email))
        {
            statusText.text = "Please enter an email address";
            return;
        }

        statusText.text = "Sending OTP...";

        try
        {
            await openfort.RequestEmailOtp(email);
            currentEmail = email;
            isEmailFlow = true;

            statusText.text = $"OTP sent to {email}. Check your inbox.";
            ShowOtpInput(true);
        }
        catch (Exception e)
        {
            statusText.text = $"Failed to send OTP: {e.Message}";
        }
    }

    private async UniTaskVoid HandleVerifyEmailOtp()
    {
        string otp = emailOtpInput.text;
        if (string.IsNullOrEmpty(otp))
        {
            statusText.text = "Please enter the OTP code";
            return;
        }

        statusText.text = "Verifying...";

        try
        {
            var response = await openfort.LogInWithEmailOtp(currentEmail, otp);

            statusText.text = $"Login successful! User: {response.User.Id}";
            Debug.Log($"Email verified: {response.User.EmailVerified}");

            // Navigate to game or next screen
            OnLoginSuccess(response);
        }
        catch (Exception e)
        {
            statusText.text = $"Verification failed: {e.Message}";
        }
    }

    private async UniTaskVoid HandleSendPhoneOtp()
    {
        string phone = phoneInput.text;
        if (string.IsNullOrEmpty(phone))
        {
            statusText.text = "Please enter a phone number";
            return;
        }

        // Ensure phone number has country code
        if (!phone.StartsWith("+"))
        {
            statusText.text = "Please include country code (for example, +1234567890)";
            return;
        }

        statusText.text = "Sending OTP...";

        try
        {
            await openfort.RequestPhoneOtp(phone);
            currentPhone = phone;
            isEmailFlow = false;

            statusText.text = $"OTP sent to {phone}. Check your messages.";
            ShowOtpInput(false);
        }
        catch (Exception e)
        {
            statusText.text = $"Failed to send OTP: {e.Message}";
        }
    }

    private async UniTaskVoid HandleVerifyPhoneOtp()
    {
        string otp = phoneOtpInput.text;
        if (string.IsNullOrEmpty(otp))
        {
            statusText.text = "Please enter the OTP code";
            return;
        }

        statusText.text = "Verifying...";

        try
        {
            var response = await openfort.LogInWithPhoneOtp(currentPhone, otp);

            statusText.text = $"Login successful! User: {response.User.Id}";
            Debug.Log($"Phone verified: {response.User.PhoneNumberVerified}");

            // Navigate to game or next screen
            OnLoginSuccess(response);
        }
        catch (Exception e)
        {
            statusText.text = $"Verification failed: {e.Message}";
        }
    }

    private void ShowOtpInput(bool isEmail)
    {
        otpInputPanel.SetActive(true);
        emailOtpInput.gameObject.SetActive(isEmail);
        verifyEmailOtpButton.gameObject.SetActive(isEmail);
        phoneOtpInput.gameObject.SetActive(!isEmail);
        verifyPhoneOtpButton.gameObject.SetActive(!isEmail);
    }

    private void OnLoginSuccess(AuthResponse response)
    {
        // Handle successful login
        Debug.Log($"User logged in: {response.User.Id}");
        // Navigate to main game scene, etc.
    }
}
```

## Best practices

1. **OTP Expiration**: OTPs typically expire after a few minutes. Prompt users to request a new one if expired.
2. **Rate Limiting**: Implement UI cooldowns to prevent spamming OTP requests.
3. **Phone Format**: Always require the full international phone format with country code.
4. **Error Handling**: Provide clear error messages for invalid or expired OTPs.
5. **Resend Option**: Allow users to request a new OTP if they don't receive it.
