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

# Authentication

> Complete guide to authenticating with the Stateset API

<Info>
  **Quick Start:** All API requests require authentication via API key in the `Authorization` header:

  ```bash theme={null}
  Authorization: Bearer sk_live_your_api_key_here
  ```
</Info>

Stateset provides multiple authentication methods to secure your API access, including API keys, JWT tokens, and OAuth 2.0 for different use cases.

## Authentication Overview

All data in Stateset is private by default, requiring authentication for every API request. We support multiple authentication methods:

### Supported Authentication Methods

<CardGroup cols={2}>
  <Card title="API Keys" icon="key">
    Best for server-to-server communication and backend integrations
  </Card>

  <Card title="JWT Tokens" icon="lock">
    Ideal for user-specific access and session management
  </Card>

  <Card title="OAuth 2.0" icon="shield">
    Perfect for third-party integrations and partner access
  </Card>

  <Card title="Webhook Signatures" icon="signature">
    Secure webhook delivery with HMAC signatures
  </Card>
</CardGroup>

## API Key Authentication

### Key Types and Permissions

| Key Type            | Prefix     | Use Case               | Permissions           |
| ------------------- | ---------- | ---------------------- | --------------------- |
| **Secret Key**      | `sk_live_` | Server-side operations | Full API access       |
| **Restricted Key**  | `rk_live_` | Limited scope access   | Custom permissions    |
| **Publishable Key** | `pk_live_` | Client-side operations | Read-only public data |
| **Test Key**        | `sk_test_` | Development & testing  | Sandbox environment   |

### Creating API Keys

1. Navigate to **Settings → API Keys** in your dashboard
2. Click **Create New Key**
3. Select key type and permissions
4. Copy and securely store your key

<Warning>
  API keys are shown only once. Store them securely and never expose secret keys in client-side code.
</Warning>

### Using API Keys

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.stateset.com/v1/orders \
    -H "Authorization: Bearer sk_live_your_api_key_here" \
    -H "Content-Type: application/json"
  ```

  ```javascript Node.js theme={null}
  // Using environment variables (recommended)
  const headers = {
    'Authorization': `Bearer ${process.env.STATESET_API_KEY}`,
    'Content-Type': 'application/json'
  };

  const response = await fetch('https://api.stateset.com/v1/orders', {
    headers
  });
  ```

  ```python Python theme={null}
  import os
  import requests

  headers = {
      'Authorization': f'Bearer {os.getenv("STATESET_API_KEY")}',
      'Content-Type': 'application/json'
  }

  response = requests.get(
      'https://api.stateset.com/v1/orders',
      headers=headers
  )
  ```
</CodeGroup>

### Restricted API Keys

Create keys with specific permissions for enhanced security:

```javascript theme={null}
// Creating a restricted key via API
const restrictedKey = await stateset.apiKeys.create({
  name: 'Read-only Orders Key',
  permissions: [
    'orders:read',
    'customers:read'
  ],
  expires_at: '2024-12-31T23:59:59Z'
});
```

## JWT Token Authentication

JWT tokens provide secure, stateless authentication for user sessions.

### JWT Token Structure

```json theme={null}
{
  "header": {
    "alg": "RS256",
    "typ": "JWT",
    "kid": "key_id_123"
  },
  "payload": {
    "sub": "user_123",
    "org_id": "org_456",
    "role": "admin",
    "permissions": ["orders:*", "customers:*"],
    "iat": 1704067200,
    "exp": 1704070800,
    "iss": "https://api.stateset.com"
  },
  "signature": "..."
}
```

## GraphQL API Authentication

### GraphQL Endpoint Access

Stateset GraphQL API requires authentication via HTTP headers:

```graphql theme={null}
# GraphQL endpoint
https://api.stateset.com/graphql

# Required headers
Authorization: Bearer sk_live_your_api_key
Content-Type: application/json
```

### GraphQL Request Example

<CodeGroup>
  ```javascript Apollo Client theme={null}
  import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
  import { setContext } from '@apollo/client/link/context';

  const httpLink = createHttpLink({
    uri: 'https://api.stateset.com/graphql',
  });

  const authLink = setContext((_, { headers }) => {
    const token = process.env.STATESET_API_KEY;
    return {
      headers: {
        ...headers,
        authorization: token ? `Bearer ${token}` : "",
      }
    }
  });

  const client = new ApolloClient({
    link: authLink.concat(httpLink),
    cache: new InMemoryCache()
  });
  ```

  ```python Python GraphQL theme={null}
  import requests

  query = """
    query GetOrders {
      orders(limit: 10) {
        id
        status
        total
      }
    }
  """

  response = requests.post(
      'https://api.stateset.com/graphql',
      json={'query': query},
      headers={
          'Authorization': f'Bearer {api_key}',
          'Content-Type': 'application/json'
      }
  )
  ```
</CodeGroup>

## OAuth 2.0 Authentication

For third-party integrations and partner access, we support OAuth 2.0:

### OAuth Flow

<Steps>
  <Step title="Authorization Request">
    ```bash theme={null}
    GET https://api.stateset.com/oauth/authorize?
      client_id=YOUR_CLIENT_ID&
      redirect_uri=YOUR_REDIRECT_URI&
      response_type=code&
      scope=orders:read customers:read&
      state=RANDOM_STATE
    ```
  </Step>

  <Step title="Token Exchange">
    ```bash theme={null}
    POST https://api.stateset.com/oauth/token
    Content-Type: application/x-www-form-urlencoded

    grant_type=authorization_code&
    code=AUTH_CODE&
    client_id=YOUR_CLIENT_ID&
    client_secret=YOUR_CLIENT_SECRET&
    redirect_uri=YOUR_REDIRECT_URI
    ```
  </Step>

  <Step title="Use Access Token">
    ```bash theme={null}
    GET https://api.stateset.com/v1/orders
    Authorization: Bearer ACCESS_TOKEN
    ```
  </Step>
</Steps>

### Available Scopes

| Scope             | Description              |
| ----------------- | ------------------------ |
| `orders:read`     | Read order data          |
| `orders:write`    | Create and update orders |
| `customers:read`  | Read customer data       |
| `customers:write` | Manage customers         |
| `inventory:read`  | View inventory levels    |
| `inventory:write` | Update inventory         |
| `returns:*`       | Full returns access      |
| `admin`           | Full API access          |

## Role-Based Access Control (RBAC)

### User Roles and Permissions

Stateset implements fine-grained permissions using role-based access control:

| Role             | Description          | Default Permissions        | API Key Prefix |
| ---------------- | -------------------- | -------------------------- | -------------- |
| **anonymous**    | Unauthenticated user | Public read-only endpoints | N/A            |
| **viewer**       | Read-only access     | All read operations        | `rk_view_`     |
| **operator**     | Standard user        | CRUD on assigned resources | `sk_live_`     |
| **manager**      | Team manager         | CRUD on team resources     | `sk_mgr_`      |
| **admin**        | Full access          | All operations             | `sk_admin_`    |
| **super\_admin** | System admin         | System configuration       | `sk_super_`    |

### Permission Matrix

| Resource  | Anonymous | Viewer | Operator   | Manager       | Admin      |
| --------- | --------- | ------ | ---------- | ------------- | ---------- |
| Orders    | ❌         | Read   | CRUD (own) | CRUD (team)   | CRUD (all) |
| Customers | ❌         | Read   | Read (own) | CRUD (team)   | CRUD (all) |
| Inventory | Read      | Read   | Read       | CRUD          | CRUD       |
| Returns   | ❌         | Read   | CRUD (own) | CRUD (team)   | CRUD (all) |
| Reports   | ❌         | Read   | Read (own) | Read (team)   | CRUD       |
| Settings  | ❌         | ❌      | Read (own) | Update (team) | CRUD       |

### Custom Permissions with Session Variables

Implement fine-grained access control using session variables:

#### JWT Claims Structure

```json theme={null}
{
  "https://hasura.io/jwt/claims": {
    "x-hasura-org-id": "org_123",
    "x-hasura-user-id": "user_456",
    "x-hasura-default-role": "operator",
    "x-hasura-allowed-roles": ["viewer", "operator", "manager"],
    "x-hasura-team-id": "team_789",
    "x-hasura-permissions": [
      "orders:read",
      "orders:write",
      "customers:read"
    ]
  },
  "iat": 1704067200,
  "exp": 1704070800
}
```

#### Permission Checks

```javascript theme={null}
// Middleware for permission checking
function requirePermission(permission) {
  return (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    
    const claims = decoded['https://hasura.io/jwt/claims'];
    const permissions = claims['x-hasura-permissions'] || [];
    
    if (!permissions.includes(permission)) {
      return res.status(403).json({
        error: 'INSUFFICIENT_PERMISSIONS',
        message: `Missing required permission: ${permission}`
      });
    }
    
    req.user = {
      id: claims['x-hasura-user-id'],
      org_id: claims['x-hasura-org-id'],
      role: claims['x-hasura-default-role'],
      permissions
    };
    
    next();
  };
}

// Usage
app.post('/api/orders', 
  requirePermission('orders:write'), 
  createOrderHandler
);
```

## Webhook Authentication

### Webhook Signature Verification

All webhooks from Stateset are signed for security:

```javascript theme={null}
const crypto = require('crypto');

class WebhookVerifier {
  constructor(secret) {
    this.secret = secret;
  }
  
  verify(payload, signature) {
    // Parse signature header
    const elements = signature.split(' ');
    const timestamp = elements.find(e => e.startsWith('t=')).slice(2);
    const signatures = elements
      .filter(e => e.startsWith('v1='))
      .map(e => e.slice(3));
    
    // Check timestamp (5 minute tolerance)
    const currentTime = Math.floor(Date.now() / 1000);
    if (currentTime - parseInt(timestamp) > 300) {
      throw new Error('Webhook timestamp expired');
    }
    
    // Compute expected signature
    const signedPayload = `${timestamp}.${payload}`;
    const expectedSig = crypto
      .createHmac('sha256', this.secret)
      .update(signedPayload)
      .digest('hex');
    
    // Timing-safe comparison
    const valid = signatures.some(sig => 
      crypto.timingSafeEqual(
        Buffer.from(sig),
        Buffer.from(expectedSig)
      )
    );
    
    if (!valid) {
      throw new Error('Invalid webhook signature');
    }
    
    return JSON.parse(payload);
  }
}

// Express middleware
const webhookAuth = (req, res, next) => {
  const verifier = new WebhookVerifier(process.env.WEBHOOK_SECRET);
  
  try {
    req.body = verifier.verify(
      req.rawBody,
      req.headers['stateset-signature']
    );
    next();
  } catch (error) {
    res.status(401).json({ error: 'Webhook verification failed' });
  }
};
```

## Security Best Practices

<AccordionGroup>
  <Accordion title="API Key Security">
    * Store keys in environment variables, never in code
    * Use different keys for different environments
    * Rotate keys regularly (every 90 days recommended)
    * Use restricted keys with minimal permissions
    * Monitor key usage for anomalies
  </Accordion>

  <Accordion title="Token Management">
    * Implement short token lifetimes (15-30 minutes)
    * Use refresh tokens for long-lived sessions
    * Store tokens securely (httpOnly cookies)
    * Implement token revocation
    * Log all authentication events
  </Accordion>

  <Accordion title="Network Security">
    * Always use HTTPS for API calls
    * Implement IP allowlisting for production
    * Use VPN or private networks when possible
    * Enable CORS with specific origins
    * Implement rate limiting per API key
  </Accordion>
</AccordionGroup>

## Authentication Examples

### React Hook with Authentication

```jsx theme={null}
import { useState, useEffect } from 'react';
import { useAuth } from '@clerk/nextjs';

/**
 * Custom hook for authenticated Stateset API calls
 */
export const useStatesetAPI = () => {
  const { getToken, isSignedIn } = useAuth();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const apiCall = async (endpoint, options = {}) => {
    if (!isSignedIn) {
      throw new Error('User not authenticated');
    }
    
    setLoading(true);
    setError(null);
    
    try {
      const token = await getToken({ template: 'stateset' });
      
      const response = await fetch(`https://api.stateset.com/v1${endpoint}`, {
        ...options,
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
          ...options.headers
        }
      });
      
      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || 'API request failed');
      }
      
      const data = await response.json();
      return data;
      
    } catch (err) {
      setError(err);
      throw err;
    } finally {
      setLoading(false);
    }
  };
  
  return { apiCall, loading, error };
};

// Usage
function OrderList() {
  const { apiCall } = useStatesetAPI();
  const [orders, setOrders] = useState([]);
  
  useEffect(() => {
    apiCall('/orders')
      .then(data => setOrders(data.orders))
      .catch(console.error);
  }, []);
  
  return (
    <div>
      {orders.map(order => (
        <OrderCard key={order.id} order={order} />
      ))}
    </div>
  );
}
```

### Python Authentication Manager

```python theme={null}
import os
import time
import requests
from typing import Optional, Dict, Any
from functools import wraps

class StatesetAuth:
    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.getenv('STATESET_API_KEY')
        self.base_url = 'https://api.stateset.com/v1'
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        })
    
    def validate_key(self) -> bool:
        """Validate API key is active"""
        try:
            response = self.session.get(f'{self.base_url}/auth/validate')
            return response.status_code == 200
        except:
            return False
    
    def with_retry(self, max_retries: int = 3):
        """Decorator for automatic retry with exponential backoff"""
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                for attempt in range(max_retries):
                    try:
                        return func(*args, **kwargs)
                    except requests.exceptions.RequestException as e:
                        if attempt == max_retries - 1:
                            raise
                        wait_time = 2 ** attempt
                        time.sleep(wait_time)
                return None
            return wrapper
        return decorator
    
    @with_retry(max_retries=3)
    def api_request(
        self,
        method: str,
        endpoint: str,
        data: Optional[Dict[Any, Any]] = None
    ) -> Dict[Any, Any]:
        """Make authenticated API request"""
        url = f'{self.base_url}{endpoint}'
        response = self.session.request(method, url, json=data)
        response.raise_for_status()
        return response.json()

# Usage
auth = StatesetAuth()
if auth.validate_key():
    orders = auth.api_request('GET', '/orders')
    print(f"Found {len(orders['data'])} orders")
```

## Troubleshooting Authentication

### Common Issues and Solutions

| Issue                   | Cause                        | Solution                        |
| ----------------------- | ---------------------------- | ------------------------------- |
| `401 Unauthorized`      | Invalid or expired API key   | Check key validity in dashboard |
| `403 Forbidden`         | Insufficient permissions     | Verify key has required scopes  |
| `429 Too Many Requests` | Rate limit exceeded          | Implement exponential backoff   |
| `CORS Error`            | Cross-origin request blocked | Use server-side proxy or SDK    |
| `Signature Mismatch`    | Invalid webhook secret       | Verify webhook secret matches   |

### Debug Authentication

```bash theme={null}
# Test API key validity
curl -I https://api.stateset.com/v1/auth/validate \
  -H "Authorization: Bearer YOUR_API_KEY"

# Check key permissions
curl https://api.stateset.com/v1/auth/permissions \
  -H "Authorization: Bearer YOUR_API_KEY"
```

***

**Next Steps:** [Create your first API request →](/api-reference/quickstart)
