# Authentication

pdf-mcp has two user-facing authentication paths:

| Use case | Recommended authentication |
|---|---|
| REST API, n8n, Make, Zapier, server-side code | API key |
| Interactive remote MCP client | OAuth through the client's browser sign-in flow |
| MCP client with custom bearer headers but no OAuth support | API key fallback |

In the [workspace release](https://pdf-mcp.io/docs/workspaces), each key or OAuth connection belongs to one workspace and acts as its creator. Current membership, role, and OAuth scopes limit every operation. Switching workspaces in the dashboard does not retarget credentials.

## API keys

### Create a key

1. Sign in at [app.pdf-mcp.io](https://app.pdf-mcp.io).
2. Select the intended workspace, then open **API Keys**.
3. Select **Create New API Key** and give it a descriptive name.
4. Copy the key immediately; the full value is shown only once.

Keys currently use this format:

```text
pdfmcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

There are no separate `pdfmcp_live_` or `pdfmcp_test_` prefixes.

### Send a key

Use the HTTP `Authorization` header:

```bash
curl -X POST https://api.pdf-mcp.io/htmlToPdf \
  -H "Authorization: Bearer pdfmcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Hello</h1>","storage":{"mode":"memory"}}' \
  --output hello.pdf
```

The same bearer header works on `/mcp` only when the MCP client supports custom HTTP headers. OAuth is safer and easier for interactive clients because the client can store and refresh tokens without exposing a long-lived API key in configuration.

### Protect and revoke keys

- Keep keys in server-side secret storage or environment variables.
- Never put a key in browser code or a version-controlled MCP configuration.
- Use separate keys for separate systems so one integration can be revoked without affecting the others.
- Revoke a key from **API Keys** immediately if it is exposed.

## OAuth for remote MCP clients

The hosted MCP endpoint is:

```text
https://api.pdf-mcp.io/mcp
```

For normal MCP setup, give that URL to the client and complete the browser prompt. Do not install a package, run a local command, paste an API key into a shared file, or create an OAuth app first.

The flow is discovered from standard metadata:

1. An unauthenticated `/mcp` request returns `401 Unauthorized` and a `WWW-Authenticate` header.
2. The client reads [protected-resource metadata](https://api.pdf-mcp.io/.well-known/oauth-protected-resource).
3. That document points to `https://backend.pdf-mcp.io` as the authorization server.
4. The client reads the [authorization-server metadata](https://backend.pdf-mcp.io/.well-known/oauth-authorization-server), registers through DCR when needed, and starts an Authorization Code flow with PKCE (`S256`).
5. The user signs in, selects a workspace, and grants the requested `pdf:read` and `pdf:write` access.
6. The client sends the access token as a bearer token and uses the issued refresh token after expiry.

This is the authorization model defined for HTTP transports by the [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization).

### Discovery endpoints

| Purpose | URL |
|---|---|
| Protected resource metadata | `https://api.pdf-mcp.io/.well-known/oauth-protected-resource` |
| Authorization server metadata | `https://backend.pdf-mcp.io/.well-known/oauth-authorization-server` |
| Authorization | `https://backend.pdf-mcp.io/oauth/authorize` |
| Token and refresh | `https://backend.pdf-mcp.io/oauth/token` |
| Dynamic client registration | `https://backend.pdf-mcp.io/oauth/register` |

Do not send users to `https://api.pdf-mcp.io/oauth/authorize` or `https://api.pdf-mcp.io/oauth/token`; OAuth is hosted on `backend.pdf-mcp.io`.

### Supported MCP scopes

| Scope | Purpose |
|---|---|
| `pdf:read` | Read PDF data and permitted resources in the bound workspace |
| `pdf:write` | Generate, transform, store, and manage PDFs and related resources |

The older names `pdf:generate`, `pdf:merge`, `pdf:extract`, and `credits:read` are not scopes advertised by the MCP resource server.

Scopes never elevate a workspace role. Consumers can use published templates visible to them, while authoring requires editor or admin access. Private templates are accessible only to their creator, including when other members are admins. Consumers and editors read only their own files; admins can read all files in that workspace. Membership removal revokes access, and rejoining does not revive old credentials. A conflicting `X-Workspace-Id` header is rejected.

## When to create an OAuth app

The **OAuth Apps** dashboard is for pre-registering an OAuth client. It is not required for clients that successfully use DCR, including the clients covered in the [MCP setup guides](https://pdf-mcp.io/docs/mcp-server#connect-a-client).

Create one only when the client:

- says the authorization server does not support its registration method;
- asks for a pre-registered client ID; or
- requires a fixed callback URI.

Then:

1. Copy the exact callback URI displayed by the client.
2. Ask a workspace admin to create an app under **OAuth Apps** and register that URI exactly.
3. Put the client ID in the client's documented OAuth setting.
4. Store the client secret in the client's OS-backed secret store or secure prompt when available; do not commit it to JSON.

See [MCP clients](https://pdf-mcp.io/docs/mcp-server#connect-a-client) for setup instructions, one-click installation links, and manual configuration.

## Build your own MCP client

Start from the metadata endpoints rather than hardcoding OAuth URLs. A compatible client must implement the MCP Streamable HTTP transport plus OAuth protected-resource discovery, authorization-server discovery, PKCE, bearer-token handling, and token refresh. It may use DCR against the advertised registration endpoint or pre-registered client credentials.

Use the following primary references:

- [MCP Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports)
- [MCP authorization](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
- [OAuth Authorization Server Metadata (RFC 8414)](https://www.rfc-editor.org/rfc/rfc8414)
- [OAuth Protected Resource Metadata (RFC 9728)](https://www.rfc-editor.org/rfc/rfc9728)
- [OAuth Dynamic Client Registration (RFC 7591)](https://www.rfc-editor.org/rfc/rfc7591)

## Troubleshooting

### `401 Unauthorized` from `/mcp`

Before sign-in, this is the expected discovery response. Use the client's **Connect**, **Authenticate**, or **Sign in** action. For REST requests, it means the bearer header is missing.

### `403 Forbidden`

The credential may be invalid or revoked, its member may have left the workspace, or the operation may require a higher role or scope. A conflicting workspace header also returns `403`. Confirm the workspace and permissions before reconnecting or replacing credentials.

### `402 Payment Required`

Authentication succeeded, but the bound workspace does not have enough credits for the requested operation. A workspace admin can add credits.

### OAuth callback or `invalid_client` error

Clear saved authentication and reconnect first so the client can repeat discovery and DCR. If the client requires static credentials, create an OAuth app with the exact callback URI the client reports.

### `invalid_scope`

Request only the scopes advertised by the protected resource: `pdf:read` and `pdf:write`.
