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

# API Authentication

> How to authenticate with CRAFT APIs using JWT tokens, client credentials, and the auto-generated SDKs.

# API Authentication

All CRAFT API endpoints require authentication via JWT tokens issued by Keycloak. This page covers how to obtain tokens, authenticate requests, and handle token refresh.

## Authentication Methods

<Tabs>
  <Tab title="User Tokens (OIDC)">
    For interactive applications, use the **OIDC Authorization Code flow with PKCE**:

    1. Redirect the user to the Keycloak authorization endpoint
    2. Exchange the authorization code for tokens
    3. Include the access token in API requests

    ```
    GET https://<keycloak-host>/realms/<org-id>/protocol/openid-connect/auth
      ?response_type=code
      &client_id=<client-id>
      &redirect_uri=<callback-url>
      &scope=openid email profile
      &code_challenge=<pkce-challenge>
      &code_challenge_method=S256
    ```

    After the user authenticates, exchange the code:

    ```bash theme={null}
    curl -X POST \
      "https://<keycloak-host>/realms/<org-id>/protocol/openid-connect/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=authorization_code" \
      -d "client_id=<client-id>" \
      -d "code=<authorization-code>" \
      -d "redirect_uri=<callback-url>" \
      -d "code_verifier=<pkce-verifier>"
    ```
  </Tab>

  <Tab title="Client Credentials">
    For service accounts, CI/CD pipelines, and automation, use the **client credentials grant**:

    ```bash theme={null}
    curl -X POST \
      "https://<keycloak-host>/realms/<org-id>/protocol/openid-connect/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials" \
      -d "client_id=<service-account-id>" \
      -d "client_secret=<service-account-secret>"
    ```

    Response:

    ```json theme={null}
    {
      "access_token": "<jwt-access-token>",
      "expires_in": 300,
      "token_type": "Bearer",
      "scope": "openid email profile"
    }
    ```

    <Note>
      Client credentials tokens do not include a refresh token. Obtain a new access token before expiration.
    </Note>
  </Tab>

  <Tab title="Development Tokens">
    For local development, use the `make get-token` shortcut in the em-runtime repository:

    ```bash theme={null}
    # Get a token for the org admin user
    make get-token

    # Get a platform admin token (Keycloak master realm)
    make get-platform-token
    ```

    Or use `curl` directly:

    ```bash theme={null}
    TOKEN=$(curl -s -X POST \
      "http://localhost:8080/realms/<org-id>/protocol/openid-connect/token" \
      -d "grant_type=password" \
      -d "client_id=admin-cli" \
      -d "username=admin" \
      -d "password=admin" \
      | jq -r '.access_token')
    ```

    <Warning>
      The password grant is intended for development only. Never use it in production -- use OIDC with PKCE or client credentials instead.
    </Warning>
  </Tab>
</Tabs>

## Using the Token

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

```bash theme={null}
curl -X GET "https://<platform-host>/api/governance/organizations" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>"
```

### Required Headers

| Header          | Required         | Description                |
| --------------- | ---------------- | -------------------------- |
| `Authorization` | Always           | `Bearer <access-token>`    |
| `X-Project-ID`  | Most endpoints   | UUID of the target project |
| `Content-Type`  | Write operations | `application/json`         |

<Note>
  The `X-Project-ID` header determines which project the request is scoped to. A single JWT token can access multiple projects within the same organization. The project ID is never embedded in the JWT.
</Note>

## Token Refresh

Access tokens have a short lifetime (typically 5 minutes). Use the refresh token to obtain new access tokens without re-authentication:

```bash theme={null}
curl -X POST \
  "https://<keycloak-host>/realms/<org-id>/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "client_id=<client-id>" \
  -d "refresh_token=<refresh-token>"
```

### Refresh Strategy

<Steps>
  <Step title="Monitor token expiry">
    Track the `exp` claim in the JWT payload. Plan to refresh when 75% of the token lifetime has elapsed.
  </Step>

  <Step title="Proactive refresh">
    Refresh the token before it expires to avoid 401 errors during in-flight requests.
  </Step>

  <Step title="Handle refresh failure">
    If the refresh token is expired or revoked, redirect the user to the login flow. For service accounts, obtain a new token via client credentials.
  </Step>
</Steps>

## SDK Authentication

The auto-generated SDKs follow the
[openapi-python-client](https://github.com/openapi-generators/openapi-python-client)
and `openapi-generator` patterns — an `AuthenticatedClient` with a `token` constructor
arg + per-endpoint module functions, **not** an object/fluent client. See the
[Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript) overviews
for the full client model.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from em_runtime_governance_sdk import AuthenticatedClient
    from em_runtime_governance_sdk.api.organizations import list_organizations

    client = AuthenticatedClient(
        base_url="https://<platform-host>/api/governance",
        token=access_token,
    )
    orgs = await list_organizations.asyncio(client=client)

    # Token refresh: pop the expired client and rebuild with a fresh token.
    # The SDK has no built-in onTokenExpired callback; wrap your calls in a
    # helper that catches 401, refreshes, and retries with a new client.
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Client } from '@emergence-ai/em-runtime-governance-sdk';
    import { listOrganizations } from '@emergence-ai/em-runtime-governance-sdk/api';

    const client = new Client({
      BASE: 'https://<platform-host>/api/governance',
      TOKEN: accessToken,
    });
    const orgs = await listOrganizations({ client });
    ```
  </Tab>
</Tabs>

## Token Claims

The JWT access token contains these claims used by the platform:

```json theme={null}
{
  "sub": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "iss": "https://keycloak.example.com/realms/org-123",
  "aud": "emergence-platform",
  "exp": 1712150400,
  "iat": 1712150100,
  "groups": ["admins", "developers"],
  "email": "user@example.com",
  "preferred_username": "jdoe"
}
```

| Claim    | Platform Usage                                       |
| -------- | ---------------------------------------------------- |
| `sub`    | Unique user identifier for audit logs                |
| `iss`    | Organization ID derived from realm in the issuer URL |
| `groups` | Mapped to OpenFGA roles for permission checks        |
| `exp`    | Token expiration validation                          |

## Error Responses

Authentication failures return structured error responses:

| Status | Code                  | Description                                                 |
| ------ | --------------------- | ----------------------------------------------------------- |
| `401`  | `UNAUTHENTICATED`     | No token provided or token signature invalid                |
| `401`  | `TOKEN_EXPIRED`       | Access token has expired; refresh and retry                 |
| `403`  | `FORBIDDEN`           | Token is valid but user lacks permission for this operation |
| `429`  | `RATE_LIMIT_EXCEEDED` | Too many requests; check `Retry-After` header               |

## Next Steps

<CardGroup cols={2}>
  <Card title="API Overview" icon="rocket" href="/api-reference/introduction">
    Review the full API structure, endpoints, and rate limits.
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/errors">
    Understand all error response formats and troubleshooting steps.
  </Card>

  <Card title="SSO Integration" icon="key" href="/guides/sso-integration">
    Configure SSO for enterprise identity providers.
  </Card>

  <Card title="RBAC Configuration" icon="user-shield" href="/guides/rbac-configuration">
    Set up roles and permissions for API access control.
  </Card>
</CardGroup>
