# Authenticating with Waydock

Waydock exposes a Model Context Protocol (MCP) server so AI agents can read and
act on a user's unified context (mail, meetings, calendar, tasks).

## Authentication model

There are two ways to authenticate, both accepted at the same endpoint and both
carrying the same scope catalog:

1. **OAuth 2.1** with dynamic client registration. The user clicks connect,
   approves a scoped consent screen, and your client receives tokens. Nothing to
   copy and paste. This is the path for MCP clients that can store and refresh
   tokens.
2. **Per-user bearer API key** (`wdmcp_`). The user issues a long-lived key by
   hand and pastes it into a config. Better for scripts and self-hosted
   automation than for a distributed client, and the right choice for any client
   whose config holds only static headers.

Pick by what your client can hold, not by which sounds stronger. A client with no
token store should use option 2: freezing a short-lived OAuth access token into a
static header gets you neither the safety of OAuth nor the durability of a key.

## Endpoints

- MCP endpoint: https://waydock.ai/api/mcp/stream (transport: streamable-http)
- Server card: https://waydock.ai/.well-known/mcp/server-card.json
- Manifest (tools + scopes): https://waydock.ai/api/mcp/manifest
- Health: https://waydock.ai/api/mcp/health

## Option 1: OAuth 2.1 (recommended)

Waydock runs its own OAuth 2.1 authorization server for MCP. Authorization code
flow, PKCE (S256) mandatory, public clients supported, dynamic registration open.

### Discovery

- Protected resource metadata (RFC 9728): https://waydock.ai/.well-known/oauth-protected-resource/api/mcp/stream
- The identical document is also served at the root form,
  https://waydock.ai/.well-known/oauth-protected-resource, so either way of deriving the PRM
  location resolves.
- Authorization server metadata (RFC 8414): https://waydock.ai/.well-known/oauth-authorization-server

You do not need to know any of these URLs up front. An unauthenticated request to
the MCP endpoint answers `401` with a challenge that points at the metadata:

```
WWW-Authenticate: Bearer resource_metadata="https://waydock.ai/.well-known/oauth-protected-resource/api/mcp/stream"
```

### 1. Register (RFC 7591)

```
POST https://waydock.ai/api/oauth/register
Content-Type: application/json
```

Open and unauthenticated: a client registers before any user is involved. Rate
limited per source IP. Send your client metadata (`client_name`,
`redirect_uris`, optionally `scope` to pre-narrow what you will ever ask for)
and you get back a `client_id`, plus a `client_secret` if you register as a
confidential client.

Supported client authentication at the token and revocation endpoints:
`none` (public client, secured by PKCE), `client_secret_basic`,
`client_secret_post`.

### 2. Authorize

```
GET https://waydock.ai/oauth/authorize
```

| Parameter | Notes |
| --- | --- |
| `response_type` | `code` only |
| `client_id` | from registration |
| `redirect_uri` | must EXACTLY match a registered URI (no normalisation) |
| `code_challenge` | required; PKCE is mandatory |
| `code_challenge_method` | `S256` only, `plain` is rejected |
| `scope` | space delimited; intersected with the catalog and your registered scope |
| `resource` | optional (RFC 8707); if sent it must equal `https://waydock.ai/api/mcp/stream` |
| `state` | optional, max 512 characters |

This is a browser page, not a JSON endpoint. The signed-in user sees a consent
screen naming your client and the permissions requested. Anything that can reach
another person (sending mail, Teams, Telegram) is always listed as its own line
item and never collapsed into a summary. On approval we redirect back to your
`redirect_uri` with `code` and your `state`. The code is single use and
expires in 120 seconds.

### 3. Exchange for tokens

```
POST https://waydock.ai/api/oauth/token
Content-Type: application/x-www-form-urlencoded
```

- `grant_type=authorization_code` with `code`, `redirect_uri`, `code_verifier`, `client_id`
- `grant_type=refresh_token` with `refresh_token`

Access tokens are prefixed `wdat_` and live 1 hour. Refresh tokens are
prefixed `wdrt_`, live 30 days, and rotate on every use: presenting a refresh
token that has already been rotated revokes the whole grant, so store only the
newest one. The response echoes the `resource` your token is audience-bound to.

### 4. Call the MCP server

```
Authorization: Bearer wdat_xxxxxxxx
```

Refresh it before it expires and keep calling. Do **not** write a `wdat_` token
into a config file: an `Authorization` header pasted into an `mcpServers` entry
is not an OAuth client, it is a credential that stops working within the hour and
has nothing to renew it. If your client cannot store and refresh tokens, and its
config holds only static headers, use a per-user API key (Option 2) instead. It is
long-lived, revocable, and carries the same scopes.

### Disconnecting

- Your client can revoke: `POST https://waydock.ai/api/oauth/revoke` with `token=<access or refresh token>`
  (RFC 7009). This ends the entire grant, both token families, not just the token you sent.
- The user can revoke at any time from **Settings → Account → MCP**
  (https://waydock.ai/settings/account/mcp), where every connected app is listed with the
  scopes it holds and a one-click revoke.

## Option 2: per-user API key

Each user issues and revokes their own keys. A key carries an explicit scope set
and every call is audited.

1. Sign in to Waydock and open **Settings → Account → MCP** (https://waydock.ai/settings/account/mcp).
2. Create a key, choosing a scope preset ("Read & message yourself" or "Full
   access") or a custom scope set. Sending email to other people is always a
   separate, explicit opt-in and is never granted by a preset.
3. Copy the key (prefix `wdmcp_`). It is shown once. Keys are revocable at any
   time, and every call is recorded in a per-key audit log.

Send the key as a bearer token on every MCP request:

```
Authorization: Bearer wdmcp_xxxxxxxx
```

The `x-api-key: wdmcp_xxxxxxxx` header is also accepted.

### Example client config (Claude Desktop)

```json
{
  "mcpServers": {
    "waydock": {
      "type": "http",
      "url": "https://waydock.ai/api/mcp/stream",
      "headers": { "Authorization": "Bearer YOUR_MCP_KEY" }
    }
  }
}
```

## Scopes

Scopes are namespaced `read:*` / `write:*` and are identical for both
credential types. Outbound send to other people requires the literal
`write:mail.send` scope and is wildcard-proof: no preset or wildcard grant can
satisfy it. `write:mail.send.self` is the narrower grant that can only reach the
user's own verified inboxes. See the manifest for the full scope catalog and the
tools each scope unlocks.
