# auth.md — Authentication for AI Agents

You are an agent. Аэродром «Новинки» exposes read-only tools over the Model Context Protocol
at `https://novinki.aero/api/mcp`.

Two ways in:

- **Agentic registration** (this document): register anonymously, exchange an identity
  assertion for an access token.
- **Client credentials** (RFC 7591 + RFC 6749): see [Client credentials](#client-credentials).

---

## Step 1 — Discover

```http
GET /.well-known/oauth-protected-resource
GET /.well-known/oauth-authorization-server
```

The authorization server metadata carries the `agent_auth` block: `identity_endpoint`
and `identity_types_supported`. Only `anonymous` is supported — there are no user
accounts, so no claim ceremony is offered (`claim_supported: false`).

---

## Step 2 — Register

```http
POST /agent/identity
Content-Type: application/json

{ "type": "anonymous" }
```

**Response (200):**

```json
{
  "registration_id": "reg_...",
  "registration_type": "anonymous",
  "identity_assertion": "<service-signed JWT>",
  "assertion_expires": "2026-07-10T13:00:00+00:00",
  "pre_claim_scopes": ["aerodrome:read"],
  "claim_supported": false
}
```

---

## Step 3 — Exchange the assertion

```http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

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

**Response (200):** `{ "access_token": "...", "token_type": "Bearer", "expires_in": 3600, "scope": "aerodrome:read" }`

Re-use the same assertion to mint new access tokens until it expires. On `invalid_grant`,
restart at Step 2.

---

## Step 4 — Call the API

```http
POST /api/mcp
Authorization: Bearer <access_token>
Content-Type: application/json

{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }
```

Tools: `get_aerodrome_facts`, `get_photo_session_pricing`, `get_contacts`.

---

## Client credentials

No human, no ceremony:

```http
POST /oauth/register   { "client_name": "Your Agent" }
POST /oauth/token      grant_type=client_credentials&client_id=...&client_secret=...
```

---

## Public REST API (no authentication)

| Method | Path | Description |
| --- | --- | --- |
| GET | `/api/health` | Service status |
| GET | `/api/v1/aerodrome` | Location, runway, radio, fuel |
| GET | `/api/v1/pricing` | Photo-session tariffs, booking rules |
| GET | `/api/v1/contacts` | Contacts |

Spec: [/openapi.json](https://novinki.aero/openapi.json) · Catalog: [/.well-known/api-catalog](https://novinki.aero/.well-known/api-catalog)

---

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `issuer_not_enabled` | `/agent/identity` | ID-JAGs are not accepted. Register anonymously. |
| `service_auth_not_enabled` | `/agent/identity` | Register anonymously. |
| `invalid_grant` | `/oauth/token` | Assertion expired. Restart at Step 2. |
| `unsupported_grant_type` | `/oauth/token` | Use `client_credentials` or `jwt-bearer`. |
| `invalid_token` | `/api/mcp` | Token expired. Mint a new one at Step 3. |

## Security notes

- TLS required. Tokens are RS256, verifiable against [`/oauth/jwks`](https://novinki.aero/oauth/jwks).
- Access tokens live 1 hour; identity assertions 1 hour.
- The `aerodrome:read` scope is read-only. Booking and payments are not available via API —
  call +7 993 609-75-50.
