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

# Authentication & OAuth

> Configure static keys or set up the OAuth 2.1 authorization code flow.

Every request to the FITsociety MCP server must present a bearer credential in the `Authorization` header:

```http theme={null}
Authorization: Bearer <credential>
```

FITsociety supports two runtime credential types: **company MCP API keys** and
**OAuth access tokens**.

Runtime credentials are accepted in this header only. Query-string and body
credentials are rejected.

***

## 1. Company MCP API keys

Coaches with administrative roles (`Manager` or `Admin`) can manually generate
static API keys in the company settings panel. Static API keys always create a
company/coach grant; they are not used for client AI access.

* **Key Format:** Prefixed with `fsc_` followed by a secure hex string (e.g., `fsc_a1b2c3d4...`).
* **Expiration:** Configurable up to **365 days** (defaults to **90 days**).
* **Usage:** Provide the plaintext key directly in the `Authorization` header. It is only shown once at creation time and cannot be retrieved later.

***

## 2. OAuth 2.1 Authorization Code Flow

For third-party AI clients, FITsociety implements an OAuth authorization code
flow with PKCE (`S256`) and refresh token rotation.

Every authorization-code and refresh-token grant is bound to this exact
protected resource:

```txt theme={null}
https://mcp.fitsociety.io/mcp/v1
```

OAuth can create two grant subject types:

| Subject Type | Who Approves           | Approval Route                           | Allowed Modules                                                                                                          |
| :----------- | :--------------------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- |
| `company`    | Manager or Admin coach | `POST /app/v1/company/mcp/oauth/approve` | Company/coach modules such as `clients`, `bookings`, `invoices`, `nutrition_logs`, and `client_notes`                    |
| `client`     | Authenticated client   | `POST /app/v1/client/mcp/oauth/approve`  | Client AI modules such as `client_ai_account`, `client_ai_bookings`, `client_ai_measurements`, and `client_ai_nutrition` |

Company/coach OAuth requires the company MCP integration feature. Client OAuth
requires the same company MCP integration feature, an active client-company
relationship, enabled client AI access for that relationship, and the company
client MCP policy.

### Token Types and Lifetimes

| Token Type             | Prefix     | TTL        | Description                                 |
| :--------------------- | :--------- | :--------- | :------------------------------------------ |
| **Authorization Code** | *Internal* | 10 minutes | Used to exchange for token pairs.           |
| **Access Token**       | `fsct_`    | 1 hour     | Presented to the MCP server for tool calls. |
| **Refresh Token**      | `fscr_`    | 90 days    | Used to obtain a new access/refresh pair.   |

### Step 1: Dynamic Client Registration

Before initiating authorization, the client application must register itself.

* **Route:** `POST /mcp/v1/oauth/register`
* **Request Body:**
  ```json theme={null}
  {
    "client_name": "My AI Assistant",
    "redirect_uris": ["https://myassistant.ai/oauth/callback"]
  }
  ```
* **Response:** Returns a unique `client_id` (e.g., `mcpc_a1b2c3d4...`) with client settings.

### Step 2: Redirect to Authorize Endpoint

The AI client redirects the signed-in user to the authorization URL, supplying
the PKCE challenge parameters.

* **Route:** `GET /mcp/v1/oauth/authorize`
* **Query Parameters:**
  * `client_id`: The registered client ID.
  * `redirect_uri`: The matching callback URL.
  * `response_type`: Must be `code`.
  * `code_challenge`: The base64url SHA-256 digest of your random `code_verifier`.
  * `code_challenge_method`: Must be `S256`.
  * `resource`: Must be exactly `https://mcp.fitsociety.io/mcp/v1`.
  * `scope`: Space-separated list of requested module keys (e.g. `clients bookings` for coach access, or `client_ai_account client_ai_bookings` for client AI access). When `scope` is omitted, the consent page proposes a curated default of eleven operational company modules (`clients`, `calendar_events`, `bookings`, `subscriptions`, `credits`, `invoices`, `recurring_bookings`, `event_templates`, `calendar_tasks`, `measurements`, `goals`); request other modules explicitly, or the approving coach can enable them during consent.
  * `state`: Optional state parameter.

The `redirect_uri` must exactly match one URI registered for the OAuth client.
The same exact URI must be sent again during the authorization-code exchange.

### Step 3: Consent and Internal Approval

The authorize route redirects the signed-in user to the FITsociety consent page.

1. For company/coach access, a Manager or Admin coach selects modules, write
   tools, and consent flags. Approving calls
   `POST /app/v1/company/mcp/oauth/approve`.
2. For client AI access, the authenticated client selects only client AI
   modules and consent flags. The company MCP integration feature must be
   enabled, the client must have client AI access enabled for the active company
   relationship, and the company client MCP policy must allow the requested
   client modules. Approving calls
   `POST /app/v1/client/mcp/oauth/approve`.
3. If the company MCP integration feature is disabled, approval fails with
   `COMPANY_MCP_FEATURE_DISABLED` and the token exchange must not continue.
4. If client AI access is disabled, approval fails with
   `COMPANY_MCP_CLIENT_AI_DISABLED` and the token exchange must not continue.
5. If the company client MCP policy is disabled or no requested module is
   allowed, approval fails before any grant or authorization code is created.
6. The backend registers a persistent grant and returns a 10-minute
   authorization code.
7. The user is redirected back to the AI client's
   `redirect_uri?code=<code>&state=<state>`.

### Step 4: Exchanging the Authorization Code

The client exchanges the code for the credentials.

* **Route:** `POST /mcp/v1/oauth/token`
* **Request Body:**
  ```json theme={null}
  {
    "grant_type": "authorization_code",
    "client_id": "mcpc_a1b2...",
    "code": "code_from_callback",
    "code_verifier": "raw_code_verifier_text",
    "redirect_uri": "https://myassistant.ai/oauth/callback",
    "resource": "https://mcp.fitsociety.io/mcp/v1"
  }
  ```
* **Response:**
  ```json theme={null}
  {
    "access_token": "fsct_...",
    "token_type": "Bearer",
    "expires_in": 3600,
    "refresh_token": "fscr_...",
    "scope": "clients bookings"
  }
  ```

### Step 5: Refreshing Tokens & Rotation

When the access token expires, use the refresh token to get a new pair.

* **Route:** `POST /mcp/v1/oauth/token`
* **Request Body:**
  ```json theme={null}
  {
    "grant_type": "refresh_token",
    "client_id": "mcpc_a1b2...",
    "refresh_token": "fscr_...",
    "resource": "https://mcp.fitsociety.io/mcp/v1"
  }
  ```

<Warning>
  **Refresh Token Rotation (RTR)** Each time a refresh token is used, it is
  immediately revoked. The endpoint returns a brand-new access token and a
  brand-new refresh token. The client must store and use the new refresh token
  for the next cycle.
</Warning>

The token endpoint returns `Cache-Control: no-store` and `Pragma: no-cache`.
Refresh-token rotation is atomic: a refresh token can be claimed only once,
including when multiple application instances receive concurrent requests.
If an already rotated refresh token is presented again, FITsociety treats it as
token-family reuse and revokes the active access and refresh tokens for that
grant and OAuth client. The user must authorize the connection again.
