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

# Authentication

> How WireSocket issues and validates JWT tokens for dataplane access.

WireSocket primarily uses **OAuth 2 Machine-to-Machine (M2M) client credentials flow** to authenticate applications. For interactive management (like the dashboard), the platform uses the **Resource Owner Password** flow through trusted applications.

***

## Obtaining a Token

Call the token endpoint from your **backend server only**. Never expose your `client_secret` in browser or mobile code.

```bash theme={null}
curl -X POST https://api.wiresocket.com/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=WireSocket.API offline_access"
```

**Response:**

```json theme={null}
{
  "access_token": "eyJ...",
  "refresh_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 900
}
```

| Field        | Value                           |
| ------------ | ------------------------------- |
| `expires_in` | 900 seconds (15 minutes)        |
| `scope`      | `WireSocket.API offline_access` |

<Warning>
  `offline_access` scope is required to receive a `refresh_token`. Without it
  you cannot renew sessions without re-authenticating with your `client_secret`.
</Warning>

***

## Required Scopes & Audience

The WireSocket Data Plane strictly enforces identity verification using the standard `aud` (Audience) and `scope` claims. Your JWT must meet these criteria or the connection will be rejected with a `401 Unauthorized` error.

### 1. Audience Enforcement

The `aud` claim must be exactly `WireSocket.API`. This ensures the token was intended for the Data Plane and not for another service.

```json theme={null}
{
  "aud": "WireSocket.API"
}
```

### 2. Scope Validation

The token must include `WireSocket.API` in either the `scope` or `scp` claim. This claim is typically a space-delimited string.

```json theme={null}
{
  "scope": "WireSocket.API offline_access"
}
```

The Data Plane validates this by splitting the string and checking for the required value. If neither `scope` nor `scp` contains `WireSocket.API`, authentication fails.

### 3. Client Implementation

When your backend requests a token from the **Dashboard** using the `client_credentials` grant, you **must** request the `WireSocket.API` scope explicitly:

```bash theme={null}
curl -X POST https://api.wiresocket.com/connect/token \
  -d "grant_type=client_credentials" \
  -d "scope=WireSocket.API" \
  ...
```

***

## JWT Claims

The access token is a signed JWT. Your backend can decode it to inspect the claims — the dataplane reads these directly with no database lookup.

```json theme={null}
{
  "iss": "https://api.wiresocket.com",
  "sub": "prod_wiresocket_abc123",
  "aud": "WireSocket.API",
  "scope": "WireSocket.API offline_access",

  "tenantid": "3f9a6c6e-2d9e-4c3e-a1f1-3b2c99e6b111",
  "appId": "7b1c0d8c-2d01-4c7c-b3b1-111aaa222bbb",
  "licenseRegionCode": "aws-us-east-1",
  "dbCode": "shard-01-us-east-1",

  "allowed_domain_1": "example.com",
  "allowed_domain_2": "staging.example.com",
  "allowed_domain_3": "localhost:3000",

  "limits": {
    "maxConnections": 100,
    "maxDocuments": 500,
    "maxUsersPerDoc": 50,
    "opsPerMinute": 10000
  },

  "exp": 1740470000,
  "iat": 1740466400,
  "jti": "unique-token-id"
}
```

### Claim Reference

| Claim                  | Description                                                         |
| ---------------------- | ------------------------------------------------------------------- |
| `sub`                  | Your `client_id`                                                    |
| `tenantid`             | Your tenant GUID — used for multi-tenant isolation                  |
| `appId`                | Your application GUID                                               |
| `licenseRegionCode`    | The base region for your metadata (e.g., `aws-us-east-1`)           |
| `dbCode`               | The specific database shard identifier (e.g., `shard-01-us-east-1`) |
| `allowed_domain_1/2/3` | Domains permitted to connect to the dataplane                       |
| `limits`               | Native JSON object containing your plan limits                      |

### Plan Limits

The `limits` claim contains your plan's resource caps:

| Limit            | Description                                    |
| ---------------- | ---------------------------------------------- |
| `maxConnections` | Total concurrent WebSocket connections allowed |
| `maxDocuments`   | Total concurrent documents allowed             |
| `maxUsersPerDoc` | Maximum users in a single document session     |
| `opsPerMinute`   | Maximum CRDT operations per minute             |

***

## Token Renewal

Access tokens expire after **15 minutes**. Use the refresh token to obtain a new access token without re-authenticating with your `client_secret`.

```bash theme={null}
curl -X POST https://api.wiresocket.com/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=YOUR_REFRESH_TOKEN" \
  -d "client_id=YOUR_CLIENT_ID"
```

<Info>
  Every refresh issues a **new refresh token** and invalidates the old one.
  Store the latest refresh token after every renewal.
</Info>

### Refresh Token Expiry

Refresh tokens are valid for **7 days**. If a refresh token expires, you must re-authenticate using your `client_id` and `client_secret` to start a new session.

```
Access token expires (15 min)
        ↓
Renew using refresh token
        ↓
New access token + new refresh token issued
        ↓
Repeat until refresh token expires (7 days)
        ↓
Re-authenticate with client_id + client_secret
```

***

## Passing the Token to the Dataplane

When connecting your Yjs provider, pass the access token using one of two methods. Both are supported in all regions.

<Tabs>
  <Tab title="WebSocket Subprotocol (Recommended)">
    ```js theme={null}
        const provider = new WebsocketProvider(
          'wss://eu-central-1.wiresocket.net',
          'your-document-id',
          ydoc,
          {
            protocols: ['access_token', 'YOUR_ACCESS_TOKEN']
          }
        )
    ```

    Preferred — keeps the token out of server access logs and browser history.
  </Tab>

  <Tab title="URL Parameter">
    ```js theme={null}
        const provider = new WebsocketProvider(
          'wss://eu-central-1.wiresocket.net?token=YOUR_ACCESS_TOKEN',
          'your-document-id',
          ydoc
        )
    ```
  </Tab>
</Tabs>

***

## Allowed Domains

Each app supports up to **3 allowed domains**. This is a critical security feature designed to prevent **Token Theft** in browser-based applications.

### How it works

1. **Origin Enforcement**: When a client connects to a node, the Data Plane checks the browser's `Origin` header.
2. **JWT Validation**: The node compares this `Origin` against the `allowed_domain` claims baked into your JWT.
3. **Automatic Rejection**: If the domain does not match (e.g., a token stolen and used on `evil.com`), the connection is immediately rejected.

### Configuration Rules

* **Normalization**: The **Dashboard** automatically strips protocols (`https://`) and trailing slashes. Entering `https://myapp.com/` results in `myapp.com`.
* **Development Support**: You can add `localhost` or `localhost:port` to your slots during development.
* **Strict Format**: Domains are validated against standard regex patterns before saving.

| Slot               | Recommended Use                            |
| ------------------ | ------------------------------------------ |
| `allowed_domain_1` | Production (e.g., `myapp.com`)             |
| `allowed_domain_2` | Staging (e.g., `staging.myapp.com`)        |
| `allowed_domain_3` | Local development (e.g., `localhost:3000`) |

***

## Plan Cancellation & Revocation

If a plan is cancelled or an app is deleted, **existing connections are not immediately terminated**. Active sessions continue until the current access token expires. No new tokens can be issued after cancellation.

<Info>
  This means tenants have up to 15 minutes of continued access after a plan
  change takes effect. Plan accordingly if you need immediate revocation.
</Info>

Replace `{tenantId}` with your tenant GUID, available in the `tenantid` claim of your JWT. For a deep-dive into how we secure these keys, see our [Security & Hardening](/security) guide.

### Key Rotation & Multiple Keys

WireSocket **periodically rotates** your tenant's signing key. During rotation, the endpoint returns multiple public keys to facilitate a seamless transition:

| Key         | Description                                               |
| ----------- | --------------------------------------------------------- |
| Active key  | Signs all new tokens                                      |
| Retired key | Previous key, valid for a **short window** after rotation |

If you are caching the JWKS response locally, re-fetch it when signature validation fails to pick up a newly rotated key.

<Warning>
  Validate against **all keys** returned in the response, not just the first.
  Pinning to a single key will cause verification failures during the rotation
  window.
</Warning>

<Info>
  This endpoint is rate limited to **20 requests per 10 minutes**. Local caching
  is strongly recommended.
</Info>
