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

# Authentication

> Secure your API requests with tenant API keys

# Authentication

All API requests to the MLM Platform require authentication using a tenant API key.

## API Key Format

API keys are passed in the `x-tenant-api-key` HTTP header:

```bash theme={null}
x-tenant-api-key: mlm_live_abc123...
```

## Key Types

| Prefix         | Environment | Usage                   |
| -------------- | ----------- | ----------------------- |
| `mlm_live_`    | LIVE        | Production data         |
| `mlm_sandbox_` | SANDBOX     | Testing and development |

<Warning>
  Never use LIVE keys in development or testing environments.
</Warning>

## Creating API Keys

1. Navigate to **Settings** > **API Keys** in the Admin Dashboard
2. Click **Create API Key**
3. Select the environment (LIVE or SANDBOX)
4. Add an optional description
5. Click **Create**

<Info>
  API keys are shown only once at creation. Store them securely.
</Info>

## Security Best Practices

### Never Expose Keys Client-Side

API keys should only be used in server-side code:

```javascript theme={null}
// BAD - Never do this in browser code
const response = await fetch('/api/users', {
  headers: {
    'x-tenant-api-key': 'mlm_live_abc123' // Exposed to users!
  }
});

// GOOD - Use a server-side proxy
// Client calls your backend
const response = await fetch('/api/proxy/users');

// Your backend adds the API key
// server.js
app.post('/api/proxy/users', async (req, res) => {
  const response = await fetch('https://app.mlm-platform.com/api/v1/users', {
    headers: {
      'x-tenant-api-key': process.env.MLM_API_KEY // Secure!
    },
    body: JSON.stringify(req.body)
  });
  res.json(await response.json());
});
```

### Use Environment Variables

Store API keys in environment variables:

```bash theme={null}
# .env (never commit this file)
MLM_API_KEY=mlm_live_abc123...
MLM_SANDBOX_API_KEY=mlm_sandbox_xyz789...
```

```javascript theme={null}
// Use environment variables
const apiKey = process.env.NODE_ENV === 'production'
  ? process.env.MLM_API_KEY
  : process.env.MLM_SANDBOX_API_KEY;
```

### Rotate Keys Regularly

1. Create a new API key
2. Update your application to use the new key
3. Verify the new key works
4. Revoke the old key

### Monitor Key Usage

Review API key usage in the Admin Dashboard:

* Request counts
* Error rates
* Last used timestamp

## Environment Detection

Every API response includes an `X-Environment` header:

```
X-Environment: LIVE
```

or

```
X-Environment: SANDBOX
```

Use this to verify you're hitting the correct environment:

```javascript theme={null}
const response = await fetch('/api/users', {
  headers: { 'x-tenant-api-key': apiKey }
});

const environment = response.headers.get('X-Environment');
if (environment !== expectedEnvironment) {
  console.warn(`Unexpected environment: ${environment}`);
}
```

<Info>
  The environment is derived from the API key used for the request.
  You may include an `X-Environment` request header for debugging/explicitness, but the server will still derive the environment from the key.
  Always treat the response header `X-Environment` as authoritative.
</Info>

## Error Responses

### Missing API Key

```json theme={null}
{
  "error": "Authentication required",
  "code": "UNAUTHORIZED"
}
```

### Invalid API Key

```json theme={null}
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY"
}
```

### Revoked / Disabled API Key

Revoked/disabled keys are treated the same as invalid keys during validation.

```json theme={null}
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY"
}
```

## Rate Limiting

API keys are subject to rate limiting:

| Response Header         | Description                      |
| ----------------------- | -------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests per window      |
| `X-RateLimit-Remaining` | Requests remaining               |
| `X-RateLimit-Reset`     | Unix timestamp when limit resets |

When rate limited, you'll receive a `429 Too Many Requests` response:

```json theme={null}
{
  "error": "Rate limit exceeded. Try again in 60 seconds.",
  "code": "RATE_LIMITED"
}
```

Handle rate limits gracefully:

```javascript theme={null}
async function makeRequest(url, options, retries = 3) {
  const response = await fetch(url, options);
  
  if (response.status === 429 && retries > 0) {
    const retryAfter = response.headers.get('Retry-After') || 60;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
    return makeRequest(url, options, retries - 1);
  }
  
  return response;
}
```

## External Auth Providers (OIDC Federation)

For applications with existing user authentication, you can configure **external auth providers** to enable federated authentication. This allows users authenticated by your identity provider to access MLM Platform features without creating separate credentials.

### When to Use External Auth Providers

* You have an existing user base with established authentication
* You want single sign-on (SSO) between your app and the MLM Platform
* You need to integrate MLM features into your existing application

### How It Works

1. User authenticates with your identity provider (IdP)
2. Your app receives a JWT from your IdP
3. Your app exchanges the JWT for MLM Platform tokens via `/api/v1/auth/exchange`
4. User can now access MLM Platform APIs with the exchanged tokens

### Setting Up an External Auth Provider

External auth providers are configured per tenant. You can set them up:

1. **Via the Admin UI** - Navigate to **Developers** > **External Auth Providers**
2. **Via the API** - Use `POST /api/v1/admin/auth/providers`

### Required Configuration

| Field                 | Description                         | Example                                          |
| --------------------- | ----------------------------------- | ------------------------------------------------ |
| **Provider ID**       | Unique identifier used in API calls | `my-app`                                         |
| **Issuer URL**        | OIDC issuer URL (must be HTTPS)     | `https://auth.example.com`                       |
| **JWKS URI**          | JSON Web Key Set endpoint           | `https://auth.example.com/.well-known/jwks.json` |
| **Allowed Audiences** | Valid `aud` claim values            | `["api://my-app"]`                               |

### Creating Your JWKS File

A JSON Web Key Set (JWKS) is a JSON file containing the public keys used to verify JWT signatures. The MLM Platform fetches this file to validate tokens from your identity provider.

#### Option 1: Use Your Identity Provider's JWKS

Most identity providers (Auth0, Okta, Azure AD, Firebase, etc.) automatically expose a JWKS endpoint:

| Provider     | JWKS URI Format                                                                             |
| ------------ | ------------------------------------------------------------------------------------------- |
| **Auth0**    | `https://YOUR_DOMAIN.auth0.com/.well-known/jwks.json`                                       |
| **Okta**     | `https://YOUR_DOMAIN.okta.com/oauth2/default/v1/keys`                                       |
| **Azure AD** | `https://login.microsoftonline.com/YOUR_TENANT/discovery/v2.0/keys`                         |
| **Firebase** | `https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com` |
| **Cognito**  | `https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID/.well-known/jwks.json`               |

#### Option 2: Generate Your Own JWKS

If you're signing JWTs yourself, you'll need to generate a key pair and create a JWKS file.

**Using Node.js (jose library):**

```javascript theme={null}
import { generateKeyPair, exportJWK } from 'jose';
import { randomUUID } from 'crypto';

async function generateJWKS() {
  // Generate an RSA key pair
  const { publicKey, privateKey } = await generateKeyPair('RS256');
  
  // Export the public key as JWK
  const publicJwk = await exportJWK(publicKey);
  
  // Add required fields
  const jwk = {
    ...publicJwk,
    kid: randomUUID(),  // Key ID - use this when signing JWTs
    alg: 'RS256',
    use: 'sig'
  };
  
  // Create the JWKS
  const jwks = { keys: [jwk] };
  
  console.log('JWKS (host this publicly):');
  console.log(JSON.stringify(jwks, null, 2));
  
  // Export private key for signing (keep this secret!)
  const privateJwk = await exportJWK(privateKey);
  console.log('\nPrivate key (keep secret):');
  console.log(JSON.stringify({ ...privateJwk, kid: jwk.kid }, null, 2));
}

generateJWKS();
```

**Using OpenSSL:**

```bash theme={null}
# Generate RSA private key
openssl genrsa -out private.pem 2048

# Extract public key
openssl rsa -in private.pem -pubout -out public.pem

# Convert to JWK format (use an online converter or jose library)
```

#### JWKS File Structure

Your JWKS file should look like this:

```json theme={null}
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "unique-key-id",
      "use": "sig",
      "alg": "RS256",
      "n": "base64url-encoded-modulus...",
      "e": "AQAB"
    }
  ]
}
```

#### Hosting Your JWKS

1. Host the JWKS file at a publicly accessible HTTPS URL
2. Ensure the URL returns `Content-Type: application/json`
3. The endpoint must be accessible without authentication
4. Consider caching headers for performance (keys don't change often)

<Warning>
  Never include private keys in your JWKS file. Only public keys should be exposed.
</Warning>

### Token Exchange Example

```javascript theme={null}
// Exchange your IdP token for MLM Platform tokens
const response = await fetch('https://app.mlm-platform.com/api/v1/auth/exchange', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Tenant-ID': 'your-tenant-id'
  },
  body: JSON.stringify({
    provider: 'my-app',           // Your provider ID
    token: idpJwtToken            // JWT from your identity provider
  })
});

const { accessToken, refreshToken, expiresIn } = await response.json();

// Use accessToken for subsequent API calls
const userResponse = await fetch('https://app.mlm-platform.com/api/v1/members/me', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});
```

### Auto-Create Users

When `auto_create_users` is enabled (default), users are automatically created in the MLM Platform on their first token exchange. The user's email, name, and external ID are extracted from the JWT claims, so make sure these are correctly included in the JWT.

<Info>
  For detailed configuration options and troubleshooting, see the [External Auth Providers Guide](/guides/external-auth-providers).
</Info>
