# Authentication

URL: https://docs.spacerun.co/docs/platform/guides/authentication

> Bearer tokens, user versus staff access, and what not to send.

## Bearer JWT

Send the Supabase access token on every `/v1/me` and `/v1/admin` request:

```http
Authorization: Bearer <ACCESS_TOKEN>
```

The API verifies the token locally: HS256 tokens against the shared JWT secret, other algorithms (ES256) against the GoTrue JWKS. Required claims are `sub` and `exp`, and the audience must be `authenticated`. Tokens expire; refresh them with the Supabase client.

| Status | `error` | Cause |
| --- | --- | --- |
| 401 | `missing_token` | No `Authorization: Bearer ...` header. |
| 401 | `invalid_token` | Bad signature, expired, or missing `sub`/`exp`. |
| 401 | `invalid_audience` | `aud` is not `authenticated`. |

## User and staff access

| Group | Rule |
| --- | --- |
| `/v1/public` | No token. |
| `/v1/me/*` | Any signed-in user. Every action applies to the caller's own data, identified by the token subject. There are no per-permission checks. |
| `/v1/admin/*` | Signed-in user holding the permission the route requires. |

Admin routes declare one or more permissions. The caller needs **at least one** (OR semantics). Roles grant permissions; `*` grants everything and `<resource>.*` grants every permission of that resource. Missing permission returns:

```json
{ "error": "forbidden", "details": { "perm": "users.read" } }
```

When a route accepts several, `perm` lists them joined with ` | `. `GET /v1/me/permissions` returns the caller's effective permissions. Role changes take effect on the user's next token.

## Runtime keys are not for this API

The Runtime service has its own service API keys. Never send them to the Platform API, and never put them in client code. The Platform API does not return the Runtime admin key; session endpoints return only a session id, play URL and member token.
