# auth.md — agent authentication for Airport Lounge List

This file tells an autonomous agent how to register itself, sign a user in, and
call our services on that user's behalf. Everything here is self-service: no
API key request form, no email, no manual approval.

Base URL: `https://airportloungelist.com`
Authorization server: `https://mcp.airportloungelist.com`
MCP endpoint: `https://mcp.airportloungelist.com/mcp`

## What needs auth

| Capability | Auth |
|---|---|
| Browse lounges, airports, networks, amenities, access rules | None. Fetch any page with `.md` appended for clean Markdown. |
| MCP tools `search_lounges`, `get_airport_lounges`, `get_lounge_details`, `find_lounges_by_access`, `list_access_methods`, `list_networks`, `get_network_lounges`, `discover_more_flight_tools` | None. Rate limited per IP. |
| MCP tools for reading your own visits, reviews, wishlist and profile (`get_my_*`) | OAuth 2.1, `read` scope |
| MCP tools that change them (`write_review`, `mark_lounge_visited`, `add_to_wishlist`, `delete_review`, …) | OAuth 2.1, `write` scope |

If you only need lounge data, stop here — add the server and call those tools with
no token at all. Read on only to act on a specific user's account.

## Discovery

Fetch these from `https://mcp.airportloungelist.com`. The apex host filters
non-browser User-Agents, so a machine client should use the MCP host for everything
except `/oauth/authorize`, which a human loads in a browser.

- Authorization server metadata (RFC 8414): `/.well-known/oauth-authorization-server` — its
  `agent_auth` block carries the machine-readable version of step 1 below
  (`register_uri`, `identity_types_supported: ["anonymous"]`, `claim_uri`).
- Protected resource metadata (RFC 9728): `/.well-known/oauth-protected-resource/mcp`
- MCP server card: `/.well-known/mcp.json`
- Agent card (A2A): `/.well-known/agent-card.json`
- Skills: `/.well-known/skills.json`
- Service catalog (RFC 9727): `/.well-known/api-catalog`

An unauthenticated call to a personal tool returns `401` with a
`WWW-Authenticate: Bearer scope="read", resource_metadata="…"` header pointing at
the protected-resource document. Follow it. `initialize`, `tools/list` and the
public lounge tools above never return that `401`.

## 1. Register your client

Dynamic Client Registration (RFC 7591). No pre-shared secret required.

```http
POST https://mcp.airportloungelist.com/oauth/register
Content-Type: application/json

{
  "client_name": "Your Agent Name",
  "redirect_uris": ["https://your-agent.example/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}
```

The response contains `client_id`. Clients are public — there is no
`client_secret`, so PKCE is mandatory.

## 2. Send the user to authorize

```
GET https://airportloungelist.com/oauth/authorize
  ?client_id=<client_id>
  &redirect_uri=<your redirect_uri>
  &response_type=code
  &scope=read+write
  &state=<random>
  &resource=https://mcp.airportloungelist.com/mcp
  &code_challenge=<BASE64URL(SHA256(verifier))>
  &code_challenge_method=S256
```

`S256` is the only supported challenge method. `resource` (RFC 8707) binds the
token to this MCP server; send it on the token request too. The user signs in
(email + password, or Google), reviews a consent screen naming your client and
your redirect host, and is redirected back with `?code=…&state=…`.

If the user has no account yet, they can create one at
`https://airportloungelist.com/users/sign_up` — email, password and a username.
The username is what their public lounge passport is published under
(`/passport/:username`). Signing in with Google at the same URL also creates an
account. Do not attempt to create accounts programmatically; send the human
through the authorize URL and let them sign up there.

## 3. Exchange the code for a token

```http
POST https://mcp.airportloungelist.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<code>
&redirect_uri=<same redirect_uri>
&client_id=<client_id>
&code_verifier=<verifier>
&resource=https://mcp.airportloungelist.com/mcp
```

Access tokens expire after 2 hours. A `refresh_token` is issued alongside; use
`grant_type=refresh_token` to rotate. Revoke at
`POST https://mcp.airportloungelist.com/oauth/revoke`.

## 4. Call the API

```http
POST https://mcp.airportloungelist.com/mcp
Authorization: Bearer <access_token>
Content-Type: application/json
```

Scopes: `read` for lounge search, profile, visits, reviews and wishlist reads;
`write` to create or modify them. Ask for the narrowest scope you need. Calling a
write tool with a `read`-only token returns `403` with
`WWW-Authenticate: Bearer error="insufficient_scope"` — re-authorize with
`scope=read write` rather than retrying.

## Limits and etiquette

- Rate limits are per-IP: 30 requests/minute to `/mcp`, 150/minute site-wide.
  Back off on `429`; the response carries `Retry-After`.
- Identify yourself with a descriptive `User-Agent` including a contact URL.
- `/api/v1/*` is a private first-party mobile API gated on `X-API-Key`. It is
  not available to third-party agents — use the MCP server instead.
- Our own outbound requests are signable against the Ed25519 key published at
  `/.well-known/http-message-signatures-directory`.

Questions: <https://airportloungelist.com/mcp>
