# TillRadar auth.md

> Offer data, fees, the cost ranking and guides are public: the REST API and the MCP server work without registration and without a token. Sign-in (OAuth 2.1) is needed only for a reader's own "remind me before it ends" offer reminders.

## Who this is for

AI agents and MCP clients acting for one TillRadar reader who wants the agent to see, set and cancel their email reminders before UK business account offers end. For the offer data itself, do not register: call the public tools without an `Authorization` header.

## What needs a token

MCP server <https://tillradar.co.uk/mcp>:

| Tool | Scope | What it does |
|---|---|---|
| `list_my_reminders` | `reminders` | the reader's reminders: offer, end date, days before the end, next reminder dates, status |
| `add_reminder` (`offer_id`, `days_before`) | `reminders` | sets an email reminder before a live offer's end date, like the "Remind me before it ends" button on the offer card; default `days_before` [7, 1]; the reader gets an email saying it was set, with a cancel link |
| `remove_reminder` (`id` or `offer_id`) | `reminders` | cancels one reminder |

Calling these tools without a token gives `HTTP 401` with `WWW-Authenticate: Bearer resource_metadata="https://tillradar.co.uk/.well-known/oauth-protected-resource/mcp", scope="reminders"`. A token without the scope gives `HTTP 403`, `error="insufficient_scope"`. Only offers with an end date (`ends` in `list_offers`) can have a reminder.

## Discovery

1. Protected resource metadata (RFC 9728): <https://tillradar.co.uk/.well-known/oauth-protected-resource/mcp> (resource <https://tillradar.co.uk/mcp>) or <https://tillradar.co.uk/.well-known/oauth-protected-resource> (the whole site).
2. Authorization server metadata (RFC 8414): <https://tillradar.co.uk/.well-known/oauth-authorization-server> – issuer <https://tillradar.co.uk>, `agent_auth` block, JWKS <https://tillradar.co.uk/oauth/jwks.json>.

## Registration

Two methods. Both end the same way: an access token for <https://tillradar.co.uk/mcp> after the reader signs in with a link from their email and approves. There are no passwords and no API keys.

### 1. OAuth 2.1 with dynamic client registration (MCP clients)

1. `POST` <https://tillradar.co.uk/oauth/register> (RFC 7591, JSON): `redirect_uris` (https, or <http://127.0.0.1> / `localhost` / `[::1]` for native apps; the loopback port may change), `client_name`, `token_endpoint_auth_method` (`none` for public clients, or `client_secret_basic` / `client_secret_post`), `grant_types: ["authorization_code","refresh_token"]`. Answer `201` with `client_id` (and `client_secret` for a confidential client).
2. Browser: `GET` <https://tillradar.co.uk/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=reminders&state=…&code_challenge=…&code_challenge_method=S256&resource=https://tillradar.co.uk/mcp> – PKCE S256 is required, `redirect_uri` exactly as registered.
3. The reader enters their email, clicks the link (or types the 6-digit code) and sees the client's name, the return address, the scope and the resource on the consent screen.
4. Return: `redirect_uri?code=…&state=…&iss=` <https://tillradar.co.uk> (check `state` and `iss`). The code works for 3 minutes, once.
5. `POST` <https://tillradar.co.uk/oauth/token> (`application/x-www-form-urlencoded`): `grant_type=authorization_code&code=…&redirect_uri=…&code_verifier=…&client_id=…&resource=` <https://tillradar.co.uk/mcp>.

### 2. Agent registration (auth.md, `service_auth`)

When the agent knows only the reader's email and has no browser redirect:

1. `POST` <https://tillradar.co.uk/oauth/agent/identity> (JSON) `{"type":"service_auth","login_hint":"reader@example.com","client_name":"Agent name"}`. The older shape `{"type":"identity_assertion","assertion_type":"verified_email","assertion":"reader@example.com"}` is accepted too; the address is still proven by the emailed sign-in. Answer `200`: `registration_id`, `claim_token` (returned once, keep it in memory), `claim_token_expires` (1 hour), `post_claim_scopes`, `claim` = `user_code` (6 digits), `verification_uri`, `expires_in` (600), `interval` (5).
2. Show the reader `verification_uri` and `user_code` in one message. The reader opens the link, signs in with the link emailed to that address, checks what you are asking for and types `user_code` on our page (they do not send it back to you).
3. Poll `POST` <https://tillradar.co.uk/oauth/token> with `grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=…` no more often than every `interval` seconds. Answers: `authorization_pending`, `slow_down`, `expired_token` (new code: `POST` <https://tillradar.co.uk/oauth/agent/identity/claim> with `{"claim_token":"…","email":"…"}`), `access_denied` (the reader refused), and after consent a standard token response with `access_token`, `refresh_token`, `scope` and `client_id`.

## Credentials

- `Authorization: Bearer <access_token>` on every request to <https://tillradar.co.uk/mcp>. Never in the URL.
- Access token: JWT RS256 (`typ: at+jwt`), valid 3600 s, `aud` = the resource you asked for (`resource`). It does not work anywhere else.
- Refresh: `grant_type=refresh_token&refresh_token=…` (plus `client_id` or the client secret). Every use returns a new refresh token; reusing an old one revokes the whole chain. A refresh token lives 30 days from its last use, a chain at most 90 days; then the reader signs in again.
- On `HTTP 401` with `error="invalid_token"` refresh once; if that gives `invalid_grant`, register again.

## Revocation

- Agent: `POST` <https://tillradar.co.uk/oauth/revoke> (RFC 7009) `token=…&token_type_hint=refresh_token` – revoking a refresh token ends the whole chain. Always `200`.
- Reader: the "Your connected apps" page https://tillradar.co.uk/oauth/apps/ (the link also comes by email after every new connection). Removal works at once, including for access tokens already issued.

## Limits

MCP server and REST API: 60 requests per minute per IP (`429`, `Retry-After`). Client registrations: 10 an hour per IP. Sign-in emails: 3 per 15 minutes per address. Token endpoint: 60 requests a minute per IP. Email code and `user_code`: 5 attempts. Reminders: 20 active per reader, 20 changes a day.

## Data and privacy

We keep the reader's email address, the client's name, the scope and the consent dates, and for each reminder the offer id, the chosen days and the consent. Tokens, codes and links are stored only as HMAC hashes. The security log holds no plain email or IP addresses and is kept 90 days. Details: https://tillradar.co.uk/privacy-policy/. Public REST API (no token): https://tillradar.co.uk/wp-json/tr/v1. Documentation: https://tillradar.co.uk/for-agents/
