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

# API Authentication

> Authenticating with the Risk Legion API

## Overview

Risk Legion uses JWT (JSON Web Tokens) for API authentication. Tokens are obtained from Supabase Auth and must be included in all API requests.

## Obtaining Tokens

### Via Supabase Client (Recommended)

```typescript theme={null}
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_ANON_KEY
);

// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'password123'
});

if (error) throw error;

// Get access token
const token = data.session.access_token;
```

### Via Script (Testing)

For testing purposes, use the provided script:

```bash theme={null}
cd backend

python scripts/get_token.py \
  --email user@example.com \
  --password YourPassword123!
```

Output:

```
Access Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Expires: 2026-01-16T11:30:00Z
```

## Using Tokens

### HTTP Header

Include the token in the `Authorization` header:

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

### cURL Example

```bash theme={null}
curl -X GET "https://api.risklegion.com/api/v1/bras" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"
```

### JavaScript/TypeScript Example

```typescript theme={null}
const response = await fetch('https://api.risklegion.com/api/v1/bras', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
});
```

## Token Lifecycle

### Token Expiration

| Token Type    | Lifetime | Refresh           |
| ------------- | -------- | ----------------- |
| Access Token  | 1 hour   | Use refresh token |
| Refresh Token | 7 days   | Re-authenticate   |

### Automatic Refresh

The Supabase client automatically refreshes tokens:

```typescript theme={null}
// Tokens are automatically refreshed
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'TOKEN_REFRESHED') {
    // Update your token reference
    currentToken = session.access_token;
  }
});
```

### Manual Refresh

```typescript theme={null}
const { data, error } = await supabase.auth.refreshSession();
if (data.session) {
  const newToken = data.session.access_token;
}
```

## Authentication Errors

### 401 Unauthorized

Token is missing, expired, or invalid:

```json theme={null}
{
  "error": "Invalid or expired token",
  "code": "UNAUTHORIZED"
}
```

**Solutions:**

* Check if token is included in request
* Refresh the token if expired
* Re-authenticate if refresh fails

### 403 Forbidden

Token is valid but user lacks permission:

```json theme={null}
{
  "error": "Insufficient permissions for this action",
  "code": "FORBIDDEN"
}
```

**Solutions:**

* Verify user has correct role
* Check entity assignments for the user
* Contact admin for access

## Security Best Practices

<AccordionGroup>
  <Accordion title="Token Storage">
    * Store tokens securely (memory or secure storage)
    * Never store tokens in localStorage for sensitive apps
    * Clear tokens on sign out
    * Don't expose tokens in URLs or logs
  </Accordion>

  <Accordion title="Token Transmission">
    * Always use HTTPS
    * Include token only in Authorization header
    * Don't pass tokens as query parameters
    * Validate HTTPS certificates
  </Accordion>

  <Accordion title="Token Handling">
    * Validate token expiration before requests
    * Implement automatic refresh logic
    * Handle authentication errors gracefully
    * Re-authenticate when refresh fails
  </Accordion>

  <Accordion title="Server-Side">
    * Verify JWT signature on every request
    * Check token audience and issuer
    * Validate token expiration
    * Log authentication failures
  </Accordion>
</AccordionGroup>

## Test User Creation

### Create Test Users

For development and testing:

```bash theme={null}
cd backend

# Create Super Admin
python scripts/create_test_user.py \
  --email superadmin@test.com \
  --password SuperAdmin123! \
  --role super_admin

# Create Client Admin
python scripts/create_test_user.py \
  --email admin@acme.com \
  --password Admin123! \
  --role admin \
  --enterprise "ACME Corporation"

# Create Assessor
python scripts/create_test_user.py \
  --email assessor@acme.com \
  --password Assessor123! \
  --role assessor \
  --enterprise "ACME Corporation"

# Create Reviewer
python scripts/create_test_user.py \
  --email reviewer@acme.com \
  --password Reviewer123! \
  --role reviewer \
  --enterprise "ACME Corporation"
```

## Role-Based Access

Different roles have different API access:

| Role             | API Access                                 |
| ---------------- | ------------------------------------------ |
| **Super Admin**  | `/api/v1/admin/*` endpoints                |
| **Client Admin** | All enterprise endpoints                   |
| **Assessor**     | BRA, Controls, Actions (assigned entities) |
| **Reviewer**     | Read-only access (assigned entities)       |

See [RBAC](/architecture/rbac) for detailed permissions.

## Postman Setup

### Environment Variables

```json theme={null}
{
  "variables": [
    {
      "key": "base_url",
      "value": "https://api.risklegion.com"
    },
    {
      "key": "access_token",
      "value": ""
    }
  ]
}
```

### Pre-request Script

```javascript theme={null}
// Auto-refresh token before requests
const tokenExpiry = pm.environment.get('token_expiry');
const now = Date.now();

if (tokenExpiry && now > tokenExpiry - 60000) {
  // Token expiring soon, refresh it
  pm.sendRequest({
    url: pm.environment.get('supabase_url') + '/auth/v1/token?grant_type=refresh_token',
    method: 'POST',
    header: {
      'Content-Type': 'application/json',
      'apikey': pm.environment.get('supabase_anon_key')
    },
    body: {
      mode: 'raw',
      raw: JSON.stringify({
        refresh_token: pm.environment.get('refresh_token')
      })
    }
  }, function (err, res) {
    if (!err) {
      const data = res.json();
      pm.environment.set('access_token', data.access_token);
      pm.environment.set('token_expiry', Date.now() + (data.expires_in * 1000));
    }
  });
}
```

## Troubleshooting

### Common Issues

<AccordionGroup>
  <Accordion title="Token not accepted">
    * Verify token format: `Bearer <token>`
    * Check for whitespace in token
    * Ensure token is for correct environment
    * Verify token hasn't expired
  </Accordion>

  <Accordion title="403 after successful auth">
    * User may not be assigned to an enterprise
    * Check user role in `enterprise_users` table
    * Verify entity assignments for Assessors/Reviewers
  </Accordion>

  <Accordion title="Token refresh fails">
    * Refresh token may be expired (7 days)
    * User may have been deactivated
    * Session may have been revoked
    * Re-authenticate from scratch
  </Accordion>
</AccordionGroup>

## Related Documentation

* [Architecture - Authentication](/architecture/authentication)
* [Architecture - RBAC](/architecture/rbac)
* [API Introduction](/api-reference/introduction)
