> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shieldhq.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> SIWE-based authentication for SHIELD API

# Authentication

SHIELD uses Sign-In with Ethereum (SIWE) for authentication. No passwords required—just your wallet.

## SIWE Flow

```
┌──────────┐                    ┌──────────┐
│  Client  │                    │  Server  │
└────┬─────┘                    └────┬─────┘
     │                               │
     │  1. Request nonce             │
     │ ───────────────────────────────▶│
     │                               │
     │  2. Return nonce              │
     │ ◀──────────────────────────────│
     │                               │
     │  3. Sign SIWE message         │
     │  (with nonce)                 │
     │                               │
     │  4. Send signature            │
     │ ───────────────────────────────▶│
     │                               │
     │  5. Verify signature          │
     │                               │
     │  6. Return JWT session        │
     │ ◀──────────────────────────────│
     │                               │
     │  7. Use JWT for API calls       │
     │ ───────────────────────────────▶│
```

## Endpoints

### 1. Get Nonce

```http theme={null}
GET /api/signIn
```

Returns a unique nonce for signing.

**Response:**

```json theme={null}
{
  "nonce": "1234567890abcdef"
}
```

### 2. Verify Signature

```http theme={null}
POST /api/verify-siwe
Content-Type: application/json
```

**Request Body:**

```json theme={null}
{
  "message": "shield.app wants you to sign in...",
  "signature": "0x..."
}
```

The message follows the [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) SIWE standard.

**Response:**

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "address": "0x...",
  "expiresAt": "2025-02-23T10:00:00Z"
}
```

### 3. Authenticated Requests

Include the JWT in subsequent requests:

```http theme={null}
GET /api/user/links
Cookie: session=eyJhbGciOiJIUzI1NiIs...
```

Or via Authorization header:

```http theme={null}
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

## SIWE Message Format

```
shield.app wants you to sign in with your Ethereum account:
0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb

Sign this message to authenticate with SHIELD.

URI: https://app.shieldhq.xyz
Version: 1
Chain ID: 84532
Nonce: 1234567890abcdef
Issued At: 2025-02-22T10:00:00Z
```

## Client Implementation

### Using wagmi + siwe

```typescript theme={null}
import { SiweMessage } from 'siwe';
import { signMessage } from 'wagmi/actions';

async function authenticate(address: string, chainId: number) {
  // 1. Get nonce
  const nonceRes = await fetch('/api/signIn');
  const { nonce } = await nonceRes.json();

  // 2. Create SIWE message
  const message = new SiweMessage({
    domain: window.location.host,
    address,
    statement: 'Sign this message to authenticate with SHIELD.',
    uri: window.location.origin,
    version: '1',
    chainId,
    nonce,
  });

  // 3. Sign message
  const messageString = message.prepareMessage();
  const signature = await signMessage({ message: messageString });

  // 4. Verify
  const verifyRes = await fetch('/api/verify-siwe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: messageString, signature }),
  });

  return await verifyRes.json();
}
```

### Using ethers.js

```typescript theme={null}
import { SiweMessage } from 'siwe';
import { ethers } from 'ethers';

async function authenticate(signer: ethers.Signer) {
  const address = await signer.getAddress();
  const nonce = (await fetch('/api/signIn').then(r => r.json())).nonce;

  const message = new SiweMessage({
    domain: 'shield.app',
    address,
    statement: 'Sign this message to authenticate with SHIELD.',
    uri: 'https://app.shieldhq.xyz',
    version: '1',
    chainId: 84532,
    nonce,
  });

  const signature = await signer.signMessage(message.prepareMessage());

  const response = await fetch('/api/verify-siwe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      message: message.prepareMessage(),
      signature,
    }),
  });

  return await response.json();
}
```

## Session Details

| Property   | Value                 |
| ---------- | --------------------- |
| Duration   | 1 hour                |
| Storage    | HTTP-only cookie      |
| Renewal    | Automatic on activity |
| Revocation | On logout or expiry   |

## Security Considerations

### Nonce Requirements

* Must be used within 5 minutes
* Single-use only
* Cryptographically random (32 bytes)

### Signature Verification

Server verifies:

* Signature is valid
* Address matches recovered signer
* Nonce was issued by server
* Domain matches expected
* Chain ID is supported

### Replay Protection

* Nonces are single-use
* Timestamps prevent old signatures
* Sessions expire after 1 hour

## Error Codes

| Code                | Meaning                         |
| ------------------- | ------------------------------- |
| `INVALID_SIGNATURE` | Signature doesn't match address |
| `EXPIRED_NONCE`     | Nonce older than 5 minutes      |
| `USED_NONCE`        | Nonce already consumed          |
| `DOMAIN_MISMATCH`   | Message domain doesn't match    |
| `UNSUPPORTED_CHAIN` | Chain ID not supported          |

## Testing

### Test Authentication

```bash theme={null}
# 1. Get nonce
curl https://app.shieldhq.xyz/api/signIn

# 2. Sign message (use your wallet)
# 3. Send to verify-siwe
```
