# External Wallet Authentication

Connect wallets via the [Sign in With Ethereum (SIWE)](https://eips.ethereum.org/EIPS/eip-4361) standard. This authentication method is designed for users who prefer to authenticate using their external wallets. Openfort's Unity integration facilitates a secure and direct authentication process using these wallets.

## Methods

### InitSiwe

Initialize Sign-In with Ethereum (SIWE) authentication flow.

**Method Signature:**

```csharp
public async UniTask<InitSiweResponse> InitSiwe(InitSiweRequest request)
```

**Parameters:**

* `InitSiweRequest request` - SIWE initialization request containing the wallet address

**Returns:**

* `UniTask<InitSiweResponse>` - Response containing the SIWE message details

**InitSiweRequest Structure:**

```csharp
public class InitSiweRequest
{
    public string address;  // Wallet address to authenticate

    public InitSiweRequest(string address)
    {
        this.address = address;
    }
}
```

**InitSiweResponse Structure:**

```csharp
public class InitSiweResponse
{
    public string address;    // Address for SIWE
    public string nonce;      // Nonce for SIWE message
    public long expiresAt;    // Expiration timestamp
}
```

**Example:**

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

public class SiweAuthManager : MonoBehaviour
{
    private OpenfortSDK openfort;

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

    public async UniTask<InitSiweResponse> InitializeSiwe(string walletAddress)
    {
        try
        {
            var request = new InitSiweRequest(walletAddress);
            var response = await openfort.InitSiwe(request);

            Debug.Log($"SIWE initialized for address: {response.address}");
            Debug.Log($"Nonce: {response.nonce}");

            return response;
        }
        catch (Exception e)
        {
            Debug.LogError($"Error initializing SIWE: {e.Message}");
            throw;
        }
    }
}
```

### LoginWithSiwe

Authenticate the user with a signed SIWE message.

**Method Signature:**

```csharp
public async UniTask<AuthResponse> LoginWithSiwe(LoginWithSiweRequest request)
```

**Parameters:**

* `LoginWithSiweRequest request` - Request containing the signature and message details

**Returns:**

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

**LoginWithSiweRequest Structure:**

```csharp
public class LoginWithSiweRequest
{
    public string signature;        // Wallet signature
    public string message;          // SIWE message that was signed
    public string walletClientType; // e.g., "metamask", "coinbaseWallet"
    public string connectorType;    // e.g., "wallet_connect_v2", "injected"
    public string address;          // Wallet address

    public LoginWithSiweRequest(string signature, string message, string walletClientType, string connectorType, string address)
    {
        this.signature = signature;
        this.message = message;
        this.walletClientType = walletClientType;
        this.connectorType = connectorType;
        this.address = address;
    }
}
```

**Example:**

```csharp
public async UniTask<AuthResponse> LoginWithSignature(
    string signature,
    string message,
    string walletClientType,
    string connectorType,
    string address)
{
    try
    {
        var request = new LoginWithSiweRequest(signature, message, walletClientType, connectorType, address);

        var response = await openfort.LoginWithSiwe(request);

        Debug.Log($"SIWE authentication successful: {response.User.Id}");
        Debug.Log($"Session expires at: {response.Session?.ExpiresAt}");

        return response;
    }
    catch (Exception e)
    {
        Debug.LogError($"Error authenticating with SIWE: {e.Message}");
        throw;
    }
}
```

### InitLinkSiwe

Initialize SIWE wallet linking for an already authenticated user.

**Method Signature:**

```csharp
public async UniTask<InitSiweResponse> InitLinkSiwe(InitLinkSiweRequest request)
```

**Parameters:**

* `InitLinkSiweRequest request` - Request containing the wallet address to link

**Returns:**

* `UniTask<InitSiweResponse>` - Response containing the SIWE message details for signing

**InitLinkSiweRequest Structure:**

```csharp
public class InitLinkSiweRequest
{
    public string address;  // Wallet address to link

    public InitLinkSiweRequest(string address)
    {
        this.address = address;
    }
}
```

**Example:**

```csharp
public async UniTask<InitSiweResponse> InitLinkWalletToAccount(string walletAddress)
{
    try
    {
        var request = new InitLinkSiweRequest(walletAddress);
        var response = await openfort.InitLinkSiwe(request);

        Debug.Log($"Wallet linking initialized for: {response.address}");
        Debug.Log($"Nonce: {response.nonce}");

        // After getting signature, call LinkWithSiwe to complete the linking
        return response;
    }
    catch (Exception e)
    {
        Debug.LogError($"Error initializing wallet link: {e.Message}");
        throw;
    }
}
```

### LinkWithSiwe

Complete wallet linking after signing the SIWE message.

**Method Signature:**

```csharp
public async UniTask<User> LinkWithSiwe(LinkWithSiweRequest request)
```

**Parameters:**

* `LinkWithSiweRequest request` - Request containing the signature and message details

**Returns:**

* `UniTask<User>` - Updated user information with linked wallet

**LinkWithSiweRequest Structure:**

```csharp
public class LinkWithSiweRequest
{
    public string signature;        // Wallet signature
    public string message;          // SIWE message that was signed
    public string walletClientType; // e.g., "metamask", "coinbaseWallet"
    public string connectorType;    // e.g., "wallet_connect_v2", "injected"
    public string address;          // Wallet address
    public int chainId;             // Chain ID

    public LinkWithSiweRequest(string signature, string message, string walletClientType, string connectorType, string address, int chainId)
    {
        this.signature = signature;
        this.message = message;
        this.walletClientType = walletClientType;
        this.connectorType = connectorType;
        this.address = address;
        this.chainId = chainId;
    }
}
```

**Example:**

```csharp
public async UniTask<User> CompleteLinkWallet(
    string signature,
    string message,
    string walletClientType,
    string connectorType,
    string address,
    int chainId)
{
    try
    {
        var request = new LinkWithSiweRequest(signature, message, walletClientType, connectorType, address, chainId);

        var user = await openfort.LinkWithSiwe(request);

        Debug.Log($"Wallet linked successfully to user: {user.Id}");

        return user;
    }
    catch (Exception e)
    {
        Debug.LogError($"Error linking wallet: {e.Message}");
        throw;
    }
}
```

### UnlinkWallet

Remove a linked wallet from the user's account.

**Method Signature:**

```csharp
public async UniTask<User> UnlinkWallet(UnlinkWalletRequest request)
```

**Parameters:**

* `UnlinkWalletRequest request` - Request containing the wallet address to unlink

**Returns:**

* `UniTask<User>` - Updated user information without the unlinked wallet

**UnlinkWalletRequest Structure:**

```csharp
public class UnlinkWalletRequest
{
    public string address;  // Wallet address to unlink
    public int chainId;     // Chain ID of the wallet

    public UnlinkWalletRequest(string address, int chainId)
    {
        this.address = address;
        this.chainId = chainId;
    }
}
```

**Example:**

```csharp
public async UniTask<User> RemoveLinkedWallet(string walletAddress, int chainId)
{
    try
    {
        var request = new UnlinkWalletRequest(walletAddress, chainId);

        var user = await openfort.UnlinkWallet(request);

        Debug.Log($"Wallet {walletAddress} unlinked from user: {user.Id}");

        return user;
    }
    catch (Exception e)
    {
        Debug.LogError($"Error unlinking wallet: {e.Message}");
        throw;
    }
}
```

## Authentication response

Upon successful authentication, you'll receive a response containing:

```json
{
  "user": {
    "id": "usr_cc9ed2b7-c5f5-4c43-8dca-c4b104ba1762",
    "email": null,
    "name": null,
    "emailVerified": false,
    "createdAt": "2024-03-20T12:00:00Z",
    "updatedAt": "2024-03-20T12:00:00Z",
    "isAnonymous": false
  },
  "token": "eyJhbGci...",
  "session": {
    "id": "ses_...",
    "token": "eyJhbGci...",
    "userId": "usr_cc9ed2b7-c5f5-4c43-8dca-c4b104ba1762",
    "expiresAt": "2024-03-21T12:00:00Z",
    "createdAt": "2024-03-20T12:00:00Z"
  }
}
```

## Complete integration example

Here's a complete example of SIWE authentication flow:

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

public class WalletAuthUI : MonoBehaviour
{
    [SerializeField] private Button connectWalletButton;
    [SerializeField] private Button linkWalletButton;
    [SerializeField] private Button unlinkWalletButton;
    [SerializeField] private TMP_Text statusText;
    [SerializeField] private TMP_InputField walletAddressInput;

    private OpenfortSDK openfort;
    private InitSiweResponse currentSiweResponse;

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

        connectWalletButton.onClick.AddListener(() => HandleWalletConnection().Forget());
        linkWalletButton.onClick.AddListener(() => HandleLinkWallet().Forget());
        unlinkWalletButton.onClick.AddListener(() => HandleUnlinkWallet().Forget());
    }

    private async UniTaskVoid HandleWalletConnection()
    {
        statusText.text = "Initializing SIWE...";

        try
        {
            string walletAddress = walletAddressInput.text;

            // Step 1: Initialize SIWE
            var initRequest = new InitSiweRequest(walletAddress);
            currentSiweResponse = await openfort.InitSiwe(initRequest);

            statusText.text = "Please sign the message in your wallet...";

            // Step 2: Get signature from wallet (implementation depends on your wallet integration)
            string signature = await GetWalletSignature(currentSiweResponse);
            string message = BuildSiweMessage(currentSiweResponse);

            // Step 3: Authenticate with signature using LoginWithSiwe
            var authRequest = new LoginWithSiweRequest(signature, message, "metamask", "injected", walletAddress);

            var authResponse = await openfort.LoginWithSiwe(authRequest);

            statusText.text = $"Connected! User: {authResponse.User.Id}";
        }
        catch (Exception e)
        {
            statusText.text = $"Connection failed: {e.Message}";
        }
    }

    private async UniTaskVoid HandleLinkWallet()
    {
        statusText.text = "Linking wallet...";

        try
        {
            string walletAddress = walletAddressInput.text;

            // Step 1: Initialize link with InitLinkSiwe
            var linkRequest = new InitLinkSiweRequest(walletAddress);
            var linkResponse = await openfort.InitLinkSiwe(linkRequest);

            // Step 2: Get signature
            string signature = await GetWalletSignature(linkResponse);
            string message = BuildSiweMessage(linkResponse);

            // Step 3: Complete linking with LinkWithSiwe
            var completeRequest = new LinkWithSiweRequest(signature, message, "metamask", "injected", walletAddress, 1);

            var user = await openfort.LinkWithSiwe(completeRequest);

            statusText.text = $"Wallet linked to user: {user.Id}";
        }
        catch (Exception e)
        {
            statusText.text = $"Link failed: {e.Message}";
        }
    }

    private async UniTaskVoid HandleUnlinkWallet()
    {
        statusText.text = "Unlinking wallet...";

        try
        {
            string walletAddress = walletAddressInput.text;
            int chainId = 1; // Ethereum mainnet, adjust as needed

            var request = new UnlinkWalletRequest(walletAddress, chainId);

            var user = await openfort.UnlinkWallet(request);

            statusText.text = $"Wallet unlinked from user: {user.Id}";
        }
        catch (Exception e)
        {
            statusText.text = $"Unlink failed: {e.Message}";
        }
    }

    // Implement these methods based on your wallet integration
    private async UniTask<string> GetWalletSignature(InitSiweResponse siweResponse)
    {
        // Implementation depends on your wallet integration (WalletConnect, etc.)
        throw new NotImplementedException("Implement wallet signing");
    }

    private string BuildSiweMessage(InitSiweResponse siweResponse)
    {
        // Build SIWE message according to EIP-4361
        throw new NotImplementedException("Implement SIWE message building");
    }
}
```
