# Authentication

The GoSmarter API uses OAuth 2.0 with JWT (JSON Web Tokens) for authentication. All API requests must include a valid access token.

## Overview

Authentication follows the OAuth 2.0 client credentials flow:

1. Exchange your client credentials for an access token
2. Include the access token in the `Authorization` header of every API request
3. Refresh the token when it expires (typically after 1 hour)

## Getting API Credentials

Contact your GoSmarter administrator to obtain:

- **Client ID** - Your application's unique identifier
- **Client Secret** - Your application's secret key (keep this secure!)
- **Tenant ID** - Your organization's tenant identifier
- **API Scope** - The permission scope for API access

:::warning

**Never expose your client secret** in client-side code or public repositories. Store it securely in environment variables or secret management systems.

:::

## Obtaining an Access Token

### Request

```bash
POST https://{tenant}.ciamlogin.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={YOUR_CLIENT_ID}
&client_secret={YOUR_CLIENT_SECRET}
&scope=api://{API_ID}/access_as_user
```

### cURL Example

```bash
curl --request POST \
  --url 'https://36078520-1cde-40ad-940e-6f26f1a90414.ciamlogin.com/36078520-1cde-40ad-940e-6f26f1a90414/oauth2/v2.0/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_id=YOUR_CLIENT_ID' \
  --data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
  --data-urlencode 'scope=api://0399c3db-81cb-4a1e-8ff6-9fcfca8dec21/access_as_user'
```

### Response

```json
{
  "token_type": "Bearer",
  "expires_in": 3599,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1yNS1BVWl..."
}
```

## Using the Access Token

Include the access token in the `Authorization` header of every API request:

```bash
curl --request GET \
  --url 'https://your-gateway.zuplo.app/api/resource' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```

### Code Examples

#### JavaScript/Node.js

```javascript
const axios = require("axios");

// Get access token
async function getAccessToken() {
  const params = new URLSearchParams({
    grant_type: "client_credentials",
    client_id: process.env.CLIENT_ID,
    client_secret: process.env.CLIENT_SECRET,
    scope: process.env.API_SCOPE,
  });

  const response = await axios.post(process.env.TOKEN_URL, params, {
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
  });

  return response.data.access_token;
}

// Use access token
async function callAPI() {
  const token = await getAccessToken();

  const response = await axios.get(
    "https://your-gateway.zuplo.app/api/resource",
    { headers: { Authorization: `Bearer ${token}` } }
  );

  return response.data;
}
```

#### Python

```python
import requests
import os

def get_access_token():
    """Get OAuth 2.0 access token"""
    data = {
        'grant_type': 'client_credentials',
        'client_id': os.environ['CLIENT_ID'],
        'client_secret': os.environ['CLIENT_SECRET'],
        'scope': os.environ['API_SCOPE']
    }

    response = requests.post(
        os.environ['TOKEN_URL'],
        data=data,
        headers={'Content-Type': 'application/x-www-form-urlencoded'}
    )
    response.raise_for_status()

    return response.json()['access_token']

def call_api():
    """Make authenticated API call"""
    token = get_access_token()

    response = requests.get(
        'https://your-gateway.zuplo.app/api/resource',
        headers={'Authorization': f'Bearer {token}'}
    )
    response.raise_for_status()

    return response.json()
```

#### C#

```csharp
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Collections.Generic;

public class GoSmarterAPIClient
{
    private readonly HttpClient _httpClient;
    private readonly string _tokenUrl;
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly string _scope;

    public async Task<string> GetAccessTokenAsync()
    {
        var content = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["grant_type"] = "client_credentials",
            ["client_id"] = _clientId,
            ["client_secret"] = _clientSecret,
            ["scope"] = _scope
        });

        var response = await _httpClient.PostAsync(_tokenUrl, content);
        response.EnsureSuccessStatusCode();

        var result = await response.Content.ReadAsAsync<TokenResponse>();
        return result.AccessToken;
    }

    public async Task<string> CallAPIAsync(string endpoint)
    {
        var token = await GetAccessTokenAsync();

        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", token);

        var response = await _httpClient.GetAsync(endpoint);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadAsStringAsync();
    }
}
```

## Token Management

### Token Expiration

Access tokens typically expire after **1 hour** (3600 seconds). The `expires_in` field in the token response tells you the exact expiration time.

### Token Caching

To optimize performance:

1. **Cache the token** - Don't request a new token for every API call
2. **Track expiration** - Store the token with its expiration time
3. **Refresh proactively** - Get a new token before the current one expires

### Example with Caching

```javascript
class TokenManager {
  constructor() {
    this.token = null;
    this.expiresAt = null;
  }

  async getToken() {
    // Check if token is still valid
    if (this.token && this.expiresAt > Date.now() + 60000) {
      return this.token;
    }

    // Get new token
    const response = await fetch(TOKEN_URL, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        grant_type: "client_credentials",
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        scope: API_SCOPE,
      }),
    });

    const data = await response.json();
    this.token = data.access_token;
    this.expiresAt = Date.now() + data.expires_in * 1000;

    return this.token;
  }
}
```

## Security Best Practices

### Keep Credentials Secure

- ✓ Store credentials in environment variables or secure vaults
- ✓ Use HTTPS for all API requests
- ✓ Rotate client secrets regularly
- ✗ Never commit credentials to version control
- ✗ Never expose credentials in client-side code
- ✗ Never log access tokens

### Environment Variables

```bash
# .env file (never commit this!)
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
TOKEN_URL=https://your-tenant.ciamlogin.com/.../oauth2/v2.0/token
API_SCOPE=api://your-api-id/access_as_user
GATEWAY_URL=https://your-gateway.zuplo.app
```

## Troubleshooting

### Invalid Client Error

```json
{
  "error": "invalid_client",
  "error_description": "Client authentication failed"
}
```

**Solutions:**

- Verify your client ID and secret are correct
- Check that your client is registered in the OAuth provider
- Ensure you're using the correct token endpoint URL

### Invalid Scope Error

```json
{
  "error": "invalid_scope",
  "error_description": "The requested scope is invalid"
}
```

**Solutions:**

- Verify the scope matches what your client is authorized for
- Check that the API scope is correctly configured
- Contact your administrator to grant the necessary permissions

### 401 Unauthorized

**Causes:**

- Token has expired
- Token is malformed or invalid
- Missing `Authorization` header
- Wrong Bearer token format

**Solutions:**

```bash
# Correct format
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...

# Common mistakes to avoid
Authorization: eyJ0eXAiOiJKV1QiLCJhbGc...  # Missing "Bearer"
Authorization: Bearer: eyJ0eXAiOiJKV1Qi...  # Extra colon
```

### Rate Limit Exceeded

```json
{
  "error": "rate_limit_exceeded",
  "message": "Too many token requests. Try again in 60 seconds."
}
```

**Solutions:**

- Implement token caching (see above)
- Reduce the frequency of token requests
- Use exponential backoff for retries

## Next Steps

- [Quick Start Guide](/docs/quickstart) - Make your first authenticated API call
- [API Reference](/api) - Explore available endpoints
- [MCP Integration](/docs/mcp-integration) - Use authentication with AI agents
