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

# JWT Authentication

> Authenticate directly with CaseXchange using username/password plus refresh cookies

## Authentication Methods

CaseXchange exposes two distinct API surfaces, each with its own authentication mechanism:

| API                                   | Auth method      | Header                        | Use case                                          |
| ------------------------------------- | ---------------- | ----------------------------- | ------------------------------------------------- |
| **Public API** (`/api/public/v1/...`) | API key          | `X-API-Key: cxp_std_...`      | Third-party integrations, data syncs, automations |
| **Internal API** (`/api/v1/...`)      | JWT Bearer token | `Authorization: Bearer <jwt>` | CaseXchange dashboard, API key management         |

This page documents the **Internal API JWT flow**. It is primarily used for:

* The CaseXchange web dashboard
* Creating and managing Public API keys via `POST /api/v1/public-api-keys`

<Callout icon="key" color="blue">
  Most third-party integrations should use the **Public API** with API key authentication. See the [Quickstart](/quickstart) to get started. JWT authentication is only needed for CaseXchange dashboard access and API key management.
</Callout>

## Overview

JWT authentication is used by the CaseXchange web dashboard and by server integrations that need to manage API keys.

* `POST /api/v1/auth/login` exchanges credentials for an access token.
* HttpOnly cookies (`auth_token`, `refresh_token_id`) are also set for browser clients.
* Use `POST /api/v1/auth/refresh-session` to rotate short-lived tokens.

<Warning>
  Access tokens expire quickly (15 minutes). Always implement refresh logic.
</Warning>

## Login Flow

1. Collect email/password from the integration user.
2. Send the payload to `/api/v1/auth/login`.
3. Cache the returned JWT for Authorization headers.
4. Store refresh identifiers securely.

```json theme={null}
POST /api/v1/auth/login
{
  "email": "user@example.com",
  "password": "password"
}
```

Response:

```json theme={null}
{
  "meta": {
    "success": true
  },
  "data": {
    "user": {
      "id": "uuid",
      "email": "user@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "role": "member",
      "firmId": "uuid",
      "isActive": true,
      "firm": {
        "id": "uuid",
        "name": "Law Firm Name"
      }
    },
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "refresh_token_string"
  }
}
```

<Note>
  The `accessToken` should be used in the `Authorization: Bearer <token>` header for subsequent Internal API requests. The `refreshToken` can be used to obtain a new access token when it expires. HttpOnly cookies are also set for browser-based clients.
</Note>

## Using Tokens

Include the JWT in every Internal API request:

```
Authorization: Bearer <jwt>
```

## Refresh Sessions

* Endpoint: `POST /api/v1/auth/refresh-session`
* Requires the `refresh_token_id` cookie (for browser clients) or refresh token in the request body (for server-to-server).
* Returns a brand new JWT access token and rotates refresh identifiers.
* Access tokens expire after 15 minutes (configurable via `JWT_EXPIRES_IN`).
* Refresh tokens expire after 7 days (configurable via `JWT_REFRESH_EXPIRES_IN`).

### Browser-based Refresh (Cookie-based)

For browser clients, the refresh endpoint automatically uses the `refresh_token_id` cookie:

```json theme={null}
POST /api/v1/auth/refresh-session
Content-Type: application/json
Cookie: refresh_token_id=<refresh_token_id>

{}
```

Response:

```json theme={null}
{
  "meta": {
    "success": true
  },
  "data": {
    "message": "Session refreshed successfully"
  }
}
```

The response sets new HttpOnly cookies:

* `auth_token` - New JWT access token
* `refresh_token_id` - New refresh token identifier

### Server-to-Server Refresh

For server integrations, include the refresh token in the request:

```json theme={null}
POST /api/v1/auth/refresh-session
Content-Type: application/json

{
  "refreshToken": "<refresh_token_string>"
}
```

<Tip>
  Use server-side storage for refresh tokens. Browser integrations should rely on HttpOnly cookies. Always implement automatic token refresh logic to handle expired tokens gracefully.
</Tip>

<Warning>
  If refresh fails (token expired, revoked, or user inactive), the endpoint clears authentication cookies and returns a 401 error. Your application should redirect to login in this case.
</Warning>

## Creating Public API Keys via JWT

After authenticating with JWT, you can programmatically create Public API keys. This is the same mechanism the CaseXchange dashboard uses under the hood.

### Create a key

```bash theme={null}
curl -X POST https://api.casexchange.com/api/v1/public-api-keys \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Key",
    "tier": "STANDARD"
  }'
```

Supported tiers: `READ_ONLY`, `STANDARD`, `FULL`. See the [Quickstart](/quickstart) for a breakdown of what each tier permits.

Response:

```json theme={null}
{
  "meta": {
    "success": true
  },
  "data": {
    "id": "uuid",
    "name": "My Key",
    "tier": "STANDARD",
    "key": "cxp_std_abc123def456..."
  }
}
```

<Warning>
  The plaintext `key` value is only returned once at creation time. Store it securely -- you will not be able to retrieve it again.
</Warning>

### Switch to API key authentication

Once you have a Public API key, use the `X-API-Key` header for all Public API calls:

```bash theme={null}
curl https://api.casexchange.com/api/public/v1/firms/me \
  -H "X-API-Key: cxp_std_abc123def456..."
```

From this point on, the JWT session is no longer needed for day-to-day integration work. Use the API key for all Public API endpoints.

## Internal API Error Codes

All Internal API errors follow the standard error response format:

```json theme={null}
{
  "meta": {
    "success": false
  },
  "error": {
    "code": "token_expired",
    "message": "Access token has expired",
    "details": []
  }
}
```

Common authentication error codes:

* `no_token` -- Missing Authorization header
* `token_expired` -- Access token exceeded its TTL
* `token_invalid` -- Token signature or claims invalid
* `rate_limited_email` / `rate_limited_ip` -- Too many login attempts
* `account_not_fully_activated` -- User account is inactive
* `account_inactive_login_attempt` -- Login attempt on inactive account

Standard HTTP status codes:

* `400` -- Bad Request (validation errors, invalid token)
* `401` -- Unauthorized (authentication failed)
* `403` -- Forbidden (account inactive)
