# Authentication

Authentication is required for all requests to the Arthur AI platform API. This page explains how to authenticate your requests and manage your credentials securely.

Authentication is required for all requests to the Arthur AI platform. Arthur uses two distinct authentication mechanisms depending on which component you are interacting with:

* **Arthur Platform** (platform API): Keycloak OAuth2 — either human login (device authorization) or machine-to-machine / service account (client credentials)
* **Arthur Engine** (including the Arthur Agent Toolkit): API keys

***

## Logging In to the Platform

Navigate to [platform.arthur.ai](https://platform.arthur.ai) and sign in with your username and password.

> **Enterprise deployments only:** SSO (bring-your-own identity provider) is only available on enterprise deployments. If your organization does not have an enterprise deployment, you must use username/password login. See the [SSO & Identity Provider Setup](./sso-guide.md) guide for enterprise SSO configuration.

***

## Arthur Platform: OAuth2 Authentication

The Arthur Platform API uses OAuth2 via Keycloak. There are two flows depending on your use case.

### Human Login (Device Authorization)

For human-run flows — such as running scripts locally or onboarding workflows — use `DeviceAuthorizer`. This opens a browser window for you to log in with your username and password.

```python Python SDK
from arthur_client.auth import ArthurOAuthSessionAPIConfiguration, DeviceAuthorizer
from arthur_client.api_bindings import ApiClient

sess = DeviceAuthorizer(arthur_host="https://<host>").authorize()
client = ApiClient(
    configuration=ArthurOAuthSessionAPIConfiguration(session=sess)
)
```

### Machine-to-Machine / Service Account (Client Credentials)

For automated systems, CI/CD pipelines, or any non-human context, use a service account with a `client_id` and `client_secret`. Tokens are automatically refreshed with a 30-second leeway before expiration (thread-safe).

```python Python SDK
from arthur_client.auth import (
    ArthurOAuthSessionAPIConfiguration,
    ArthurClientCredentialsAPISession,
    ArthurOIDCMetadata,
)
from arthur_client.api_bindings import ApiClient

sess = ArthurClientCredentialsAPISession(
    client_id="<your-client-id>",
    client_secret="<your-client-secret>",
    metadata=ArthurOIDCMetadata(arthur_host="https://<host>"),
)
client = ApiClient(
    configuration=ArthurOAuthSessionAPIConfiguration(session=sess)
)
```

```javascript JavaScript
const response = await fetch(
  "https://<host>/realms/arthur/protocol/openid-connect/token",
  {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: "<your-client-id>",
      client_secret: "<your-client-secret>",
    }),
  }
);
const { access_token } = await response.json();

// Use the token on subsequent requests
const apiResponse = await fetch("https://<host>/api/v1/...", {
  headers: { "Authorization": `Bearer ${access_token}` },
});
```

To create a service account and obtain credentials, see the [Authorization API](./authorization-v1.md).

***

## Arthur Engine: API Key

The Arthur Engine (including the Arthur Agent Toolkit) uses API key authentication. All requests include the key as a Bearer token:

```
Authorization: Bearer <api_key>
```

### Key Types

| Type               | Source                                        | Use                 |
| ------------------ | --------------------------------------------- | ------------------- |
| Master / Admin key | `GENAI_ENGINE_ADMIN_KEY` environment variable | Admin operations    |
| User-generated key | Created via the admin API                     | Standard operations |

### Creating a User Key

Use the admin key to create a user key. The raw key is returned **once** — it is bcrypt-hashed in the database after creation, so store it immediately.

```curl cURL
curl -X POST http://<host>/auth/api_keys/ \
  -H "Authorization: Bearer <admin_key>" \
  -H "Content-Type: application/json" \
  -d '{"description": "my key"}'
```

### Using a Key

A user-generated Arthur Engine API key can also be used as your `ARTHUR_API_KEY` environment variable in the observability SDK — the same key works in both contexts.

```python Python SDK
import os
headers = {"Authorization": f"Bearer {os.environ['ARTHUR_API_KEY']}"}
```

```javascript JavaScript
const response = await fetch("http://<host>/...", {
  headers: { "Authorization": `Bearer ${process.env.ARTHUR_API_KEY}` },
});
```

```curl cURL
curl -X GET http://<host>/... \
  -H "Authorization: Bearer $ARTHUR_API_KEY"
```

### Key Details

| Detail              | Value                                                |
| ------------------- | ---------------------------------------------------- |
| Storage             | `api_keys` table, bcrypt-hashed (rounds=9)           |
| Internal format     | `base64({id}:{key})`                                 |
| In-memory cache TTL | 10 seconds per validator instance                    |
| Validation chain    | Master key → User-generated key → JWT/OAuth fallback |

***

## Error Handling

| Status Code        | Meaning                                                               |
| ------------------ | --------------------------------------------------------------------- |
| `401 Unauthorized` | Credentials are missing, invalid, or expired.                         |
| `403 Forbidden`    | Credentials are valid but lack permission for the requested resource. |

***

## Best Practices

* **Never commit credentials to version control.** Use environment variables or a secrets manager.
* **Rotate keys and secrets regularly.**
* **Use the minimum necessary permissions** — assign scoped roles via the [Authorization API](./authorization-v1.md).
* **Store user-generated API keys immediately** upon creation — they cannot be retrieved after the response is returned.