# auth.md

> Last updated: 2026-08-18

You are an agent. This document tells you how to get a credential for the
SurrealDB surfaces that require one, and how to use, scope and revoke it.
Follow the steps in order and do not POST to a registration endpoint before
reading Step 2 - one of the two methods needs a human, and picking the wrong
one wastes a round trip.

`https://surrealdb.com` (this origin) is the public marketing and
documentation site. It serves no authenticated API, issues no credentials and
never needs a token: every page here is readable anonymously, and in markdown
(append `.md` to any path, or send `Accept: text/markdown`). If you are only
reading about SurrealDB, you are done - see `https://surrealdb.com/llms.txt`.

## What needs authentication

| Surface | Resource / audience | Authorization server | Methods |
| --- | --- | --- | --- |
| MCP server (`https://mcp.surrealdb.com`, streamable HTTP) | `https://mcp.surrealdb.com` | `https://mcp.surrealdb.com` | Interactive OAuth 2.1, or personal access token |
| Platform API (`https://api.surrealdb.com`) | `https://api.surrealdb.com/api` | `https://auth.surrealdb.com/` | Interactive OAuth 2.1, or personal access token |
| A SurrealDB instance you run | your own instance | your own instance | Instance credentials, defined by you |

The first two are SurrealDB Cloud control-plane surfaces and share one account
system (Surreal ID). The third is not part of this service: root, namespace,
database and record access users live inside the instance you deployed, are
configured by you, and are documented under
`https://surrealdb.com/docs/surrealdb/security/authentication`. Nothing in this
document provisions those.

## What this service does not implement

SurrealDB does **not** implement agent-attested registration. There is no
`/agent/identity` endpoint, no `identity_assertion` intake (no ID-JAG), no
`service_auth` email provisioning, no `anonymous` identity, and no claim
ceremony. An agent cannot self-provision an account here: an account is always
created by a human, and a credential is always derived from that account. Do
not probe for those paths, and do not send ID-JAGs.

The Authorization Server metadata still carries an `agent_auth` block so
agents can discover the one registration method that exists: RFC 7591 dynamic
client registration. `register_uri` is the MCP `registration_endpoint`. There
is no `claim_uri`. Platform-API OAuth tokens can be revoked at the Auth0
tenant's `revocation_endpoint` (RFC 7009); MCP OAuth tokens have no revocation
endpoint today (see Step 5).

The two supported methods are in Step 2. Both end in the same place: a bearer
credential scoped to a real SurrealDB account.

## Step 1 - Discover

Start on this origin. Both documents are JSON at HTTP 200 (not a redirect):

```http
GET https://surrealdb.com/.well-known/oauth-protected-resource
GET https://surrealdb.com/.well-known/oauth-authorization-server
```

RFC 9728 derives the metadata URL from the resource identifier, so the
Protected Resource Metadata on this origin names `https://surrealdb.com` as
`resource` and lists it in `authorization_servers`; the Authorization Server
metadata's `issuer` is the same value. That is the discovery entry point only -
this marketing site has no authenticated API and never needs a token. The OAuth
endpoints those documents point at (`authorization_endpoint`, `token_endpoint`,
`registration_endpoint`, and `agent_auth.register_uri`) are the MCP OAuth
proxy at `https://mcp.surrealdb.com`; attach the resulting credential to that
host (see Step 4). `agent_auth.skill` is this document. There is no
`revocation_endpoint` here - MCP OAuth tokens are not revocable through this
metadata (see Step 5).

### MCP server

The MCP endpoint is the surface those OAuth endpoints protect. An
unauthenticated request is answered with an RFC 9728 challenge:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.surrealdb.com/.well-known/oauth-protected-resource"
```

The MCP host publishes its own PRM and AS metadata, with `resource` /
`issuer` `https://mcp.surrealdb.com` and the same `agent_auth` block (its
`register_uri` is that host's `registration_endpoint`):

```http
GET https://mcp.surrealdb.com/.well-known/oauth-protected-resource
GET https://mcp.surrealdb.com/.well-known/oauth-authorization-server
```

Read endpoints from the document rather than hard-coding the paths quoted
here. Clients are public (`token_endpoint_auth_methods_supported: "none"`);
PKCE `S256` is required.

### Platform API

`https://api.surrealdb.com` is a bearer-JWT API whose tokens are issued by the
Auth0 tenant at `https://auth.surrealdb.com/`, for the audience
`https://api.surrealdb.com/api`. Its metadata:

```http
GET https://api.surrealdb.com/.well-known/oauth-protected-resource/api
GET https://auth.surrealdb.com/.well-known/oauth-authorization-server
GET https://auth.surrealdb.com/.well-known/openid-configuration
```

`https://surrealdb.com/.well-known/openid-configuration` redirects to the
Auth0 tenant (this origin is not an OIDC issuer). Two caveats:

- Platform-API 401s advertise the `/api` PRM in `WWW-Authenticate`. The path
  suffix is the RFC 9728 location derived from the resource identifier
  `https://api.surrealdb.com/api`. The same document is also served at
  `https://api.surrealdb.com/.well-known/oauth-protected-resource` so a
  host-level probe still finds it; `resource` remains `<origin>/api`.
- `https://api.surrealdb.com/.well-known/oauth-authorization-server` describes
  the management MCP OAuth flow (issuer `https://api.surrealdb.com`, dynamic
  registration under `/management/mcp/oauth/`), not the tenant that issues
  platform API tokens. Do not mistake it for the platform API's authorization
  server.

The API surface itself is described by an OpenAPI 3.0 document at
`https://api.surrealdb.com/openapi/public.yaml`, with human documentation at
`https://api.surrealdb.com/docs`.

## Step 2 - Pick a method

1. **You can open a browser for the user** (an interactive client: Claude Code,
   Cursor, Claude Desktop, Windsurf, VS Code, or any MCP client with sign-in
   support) → use [interactive OAuth](#step-3a---interactive-oauth). The user
   signs in with their Surreal ID and approves the connection. Nothing is
   pasted by hand.
2. **You are headless or unattended** (CI, a cron job, a server-side agent) →
   use a [personal access token](#step-3b---personal-access-token). A human
   creates it once in the account portal and hands it to you.
3. **Neither, and no human is reachable** → stop. You cannot obtain a
   credential. Read-only work is still possible without one: the docs, this
   site's markdown views, `https://surrealdb.com/llms.txt`, and
   `ssh surrealdb.sh` (the documentation as a browsable filesystem, no auth).

## Step 3a - Interactive OAuth

Standard OAuth 2.1 with dynamic client registration. Take every endpoint from
the AS metadata fetched in Step 1.

1. **Register.** POST an RFC 7591 client registration to
   `registration_endpoint`, with your `redirect_uris` and
   `token_endpoint_auth_method: "none"`. You receive a `client_id`. Clients are
   public, so there is no client secret; persist the `client_id` and reuse it.
2. **Authorize.** Send the user to `authorization_endpoint` with
   `response_type=code`, your `client_id`, `redirect_uri`, `state`, and a PKCE
   `code_challenge` using `S256`. The user signs in with their Surreal ID (the
   same account as SurrealDB Cloud) and approves the connection. Surface what
   you are asking for before you open the browser: this is the user's only
   consent gate.
3. **Exchange.** POST the returned `code` plus your `code_verifier` to
   `token_endpoint` (`grant_type=authorization_code`). You receive an access
   token and, when granted, a refresh token.
4. **Refresh.** Use `grant_type=refresh_token` at the same endpoint. Do not
   re-run registration per session; a fresh `client_id` on every start is a bug.

## Step 3b - Personal access token

A personal access token (PAT) is a long-lived bearer credential that stands in
for the whole account, so it is created deliberately by a human:

1. The user opens `https://account.surrealdb.com/tokens`, creates a token with a
   label, selects scopes, and sets a lifetime (1 to 365 days; a token with no
   expiry is possible and discouraged).
2. The secret is shown once, prefixed `sdbp_`. It is not retrievable
   afterwards.
3. The user gives it to you through your own secret storage. Never ask for it in
   plain chat, never log it, never write it to a file the user did not name, and
   never send it anywhere other than `mcp.surrealdb.com` or
   `api.surrealdb.com`.

Grant the narrowest scope set that does the job. The documented scopes are:

| Scope | Grants |
| --- | --- |
| `read:account` | Read the authenticated user's account profile |
| `manage:account-tokens` | List, create and revoke the user's personal access tokens |
| `read:cloud` | Read Cloud organisations, instances and settings |
| `write:cloud-user` | Update the Cloud profile, record terms acceptance and answer onboarding questions |
| `write:cloud-instances` | Create, update and delete Cloud instances |
| `query:cloud-instances` | Run SurrealQL against Cloud instances |
| `write:cloud-organization` | Manage organisation membership and settings |
| `write:cloud-billing` | Manage billing and payment details |
| `write:cloud-spectron` | Manage Spectron contexts |
| `query:spectron-contexts` | Query Spectron contexts |
| `read:learn` | Read course progress and enrolment state |
| `read:support` | Read the user's Cloud support conversations, and tickets for organisations they belong to |

The authoritative, live registry is `GET /api/accounts/v1/scopes` on
`https://api.surrealdb.com` (it returns the scopes grantable to the calling
account). Tokens can also be listed, created and revoked programmatically once
you already hold a credential: `GET`, `POST` and
`DELETE /api/accounts/v1/tokens/{id}` under `/api/accounts/v1/`.

## Step 4 - Use the credential

Both surfaces take the credential in the HTTP header, and only in the header
(`bearer_methods_supported: ["header"]`). Never in a query string.

```http
Authorization: Bearer <access token or sdbp_… PAT>
```

- MCP: `https://mcp.surrealdb.com`, streamable HTTP transport. The server card
  at `https://surrealdb.com/.well-known/mcp/server-card.json` describes it;
  usage is documented at
  `https://surrealdb.com/docs/build/ai-agents/mcp`.
- Platform API: `https://api.surrealdb.com`, per the OpenAPI description above.

Treat a `401` as "credential is dead" (re-run Step 3), and a `403` as "scope is
missing" (the user must issue a credential with wider scope; do not retry the
same call).

## Step 5 - Revoke

- **PAT:** the user deletes it at `https://account.surrealdb.com/tokens`, or you
  call `DELETE /api/accounts/v1/tokens/{id}`. Revocation is immediate and is the
  correct response to any suspected leak. A PAT does not expire on its own
  unless a lifetime was set, so delete it when the job is done.
- **Platform API OAuth tokens:** the tenant advertises a `revocation_endpoint`
  (RFC 7009) in its AS metadata; read it from the document and POST the token
  there.
- **MCP OAuth tokens:** the MCP AS metadata advertises no revocation endpoint
  today. Discard your copies and have the user revoke the connection from the
  account portal.

SurrealDB sends no revocation callbacks: there is no `events_endpoint`, and no
security event token is delivered to agents. Detect revocation from a `401` on
your next call, and re-authenticate rather than retrying.

## Notes for scanners

Everything above is derived from public discovery documents and public
documentation; those remain authoritative if they disagree with this file.
Registration and token endpoints are omitted here on purpose - fetch the
metadata. Do not POST to `registration_endpoint`, `token_endpoint` or the token
API during passive scanning: registration creates client records, and token
calls can create credentials or trigger email to a real account.

Security contact: `https://surrealdb.com/.well-known/security.txt`. Machine
readable service index: `https://surrealdb.com/.well-known/api-catalog`.
Everything else an agent might want on this origin is listed in
`https://surrealdb.com/llms.txt`.
