# Agent authentication on Open Media

How an agent gets access to Open Media, and exactly which credentials exist. This file lives at `/auth.md`, the discovery path from the WorkOS auth.md spec. Open Media implements its anonymous registration; this file says plainly what it does not implement.

## Discover

- Public data needs no credential at all: `https://api.openmedia.site/open/mcp`, `GET /v1/feed`, `GET /v1/search`, `GET /v1/posts`, `GET /v1/profiles/{handle}`, `GET /v1/events/upcoming` and every discovery file.
- Protected-resource metadata (RFC 9728): [https://api.openmedia.site/.well-known/oauth-protected-resource](https://api.openmedia.site/.well-known/oauth-protected-resource).
- Authorization-server metadata (RFC 8414), including the `agent_auth` block with `identity_endpoint` and `identity_types_supported`: [https://api.openmedia.site/.well-known/oauth-authorization-server](https://api.openmedia.site/.well-known/oauth-authorization-server).
- A protected route called without a valid credential returns 401 with `WWW-Authenticate: Bearer resource_metadata="https://api.openmedia.site/.well-known/oauth-protected-resource"`.

## Pick a method

- **No credential:** everything public. Start here.
- **Anonymous agent registration** (`agent_auth`, type `anonymous`): an agent gets its own identity and an access token with the `public:read` scope, so its requests are counted in its own rate-limit bucket instead of sharing one with its IP address. It never grants access to anyone's account.
- **The person's access token (bearer):** the account API (`/v1/me/...`) and the account MCP server (`/me/mcp`). The person copies it from the app (Settings, then Developers and agents); it acts only as them.
- **OAuth 2.1 with PKCE (S256):** Open Media Events at `https://api.openmedia.site/mcp`, scopes `events:read` and `events:write`, with client ID metadata documents or Dynamic Client Registration at `https://api.openmedia.site/oauth/register`.

## Register

```
POST https://api.openmedia.site/agent/identity
Content-Type: application/json

{"type": "anonymous", "name": "my-agent"}
```

The response holds `registration_id`, `identity_assertion` (valid for 30 days), `assertion_expires`, `pre_claim_scopes` (`public:read`) and `post_claim_scopes` (empty). Only the `anonymous` type is supported: `service_auth` and `identity_assertion` (ID-JAG) registration return `unsupported_identity_type`.

## Claim

There is no claim ceremony and no `claim_endpoint`: an anonymous agent identity is never linked to a person. To act for a person, use their own access token instead.

## Exchange

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<identity_assertion>
```

The response is `{"access_token": "…", "token_type": "Bearer", "expires_in": 3600, "scope": "public:read"}`. Exchange the same assertion again when the access token expires. OAuth clients for Open Media Events exchange authorization codes and refresh tokens at the same endpoint.

## Use the access_token

```
Authorization: Bearer <access_token>
```

Agent tokens work on every public route and the public MCP server. Account routes need the person's own token and answer 401 to an agent token.

## Errors

- `400 invalid_grant`: the identity assertion is invalid or expired. Register again.
- `400 unsupported_identity_type`: only `{"type": "anonymous"}` is accepted.
- `401 unauthorized` with `WWW-Authenticate`: missing, expired or invalid token on a protected route.
- `429 rate_limited` with `Retry-After`: back off for the stated seconds.

## Revocation

Agent identity assertions expire after 30 days and access tokens after an hour; there is no `events_endpoint` for revocation events. Account tokens end when they expire or the person signs out. OAuth tokens for Open Media Events are revoked at `https://api.openmedia.site/oauth/token`, the advertised revocation endpoint.
