# Train Badger auth.md

Audience: AI agents (Muse, Claude, ChatGPT, any MCP or REST client) acting for a Train Badger account holder.

## Two ways in — both end at the same account and the same three scopes

1. **OAuth 2.1 (primary, one-click).** Train Badger is the authorization server for its own API. Issuer `https://api.trainbadger.app`; metadata at `https://api.trainbadger.app/.well-known/oauth-authorization-server` (RFC 8414); protected-resource metadata at `https://api.trainbadger.app/.well-known/oauth-protected-resource` and `/.well-known/oauth-protected-resource/mcp` (RFC 9728). An unauthenticated `POST https://api.trainbadger.app/mcp` answers `401` with `WWW-Authenticate: Bearer resource_metadata="https://api.trainbadger.app/.well-known/oauth-protected-resource/mcp", scope="delay-repay:read"`, which is all an MCP client needs to run the flow.
2. **Personal API key (manual, for scripts and header-only clients).** The account holder mints a `tb_live_…` key at https://trainbadger.app/account-keys and pastes it into the agent's config.

Scopes (identical for both): `delay-repay:read` (always), `delay-repay:write` (mark claims filed), `delay-repay:track` (pre-register travel).

## OAuth profile (what a client must do)

- Register once: `POST https://api.trainbadger.app/oauth/register` (RFC 7591, open, public clients only — `token_endpoint_auth_method: "none"`), with `client_name` and `redirect_uris` (absolute `https://`, or `http://localhost` / `http://127.0.0.1` for native apps; no fragments). You get a `client_id`, no secret. 10 registrations per hour per IP.
- Authorize: send the user to `https://trainbadger.app/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=delay-repay:read%20delay-repay:write%20delay-repay:track&state=…&code_challenge=…&code_challenge_method=S256&resource=https%3A%2F%2Fapi.trainbadger.app%2Fmcp`. PKCE `S256` is required (`plain` is refused). `resource` (RFC 8707) is required and must be `https://api.trainbadger.app/mcp` or `https://api.trainbadger.app`. The user signs in (verified email required) and approves on a consent page that shows your `client_name` and lets them untick `write` / `track`. The redirect carries `code`, `state` and `iss`.
- Exchange: `POST https://api.trainbadger.app/oauth/token` (form-encoded) with `grant_type=authorization_code`, `code`, `redirect_uri`, `client_id`, `code_verifier`. Codes are single-use and live 60 s; reusing one revokes every token issued from it.
- Use: `Authorization: Bearer tb_oa_…` on every request to the resource you named in `resource`. A token is valid **only** for that audience: one issued for `https://api.trainbadger.app/mcp` is rejected by the REST API and vice versa (`401`). Access tokens last 1 hour.
- Refresh: `grant_type=refresh_token` with `refresh_token` and `client_id`. Refresh tokens last 30 days and rotate on every use; presenting a spent one revokes the whole grant.
- Revoke: `POST https://api.trainbadger.app/oauth/revoke` with `token` and `client_id` (RFC 7009). The user can also disconnect you at any time from https://trainbadger.app/account-keys; expect `401` afterwards and re-run the flow.

There is **no automated agent registration of accounts**: dynamic client registration registers your software, never a person. Every grant and every key belongs to an email-verified human who attests to the journeys the agent registers (see the terms, "API and agents").

## How a key is obtained (manual path)

1. The account holder registers at https://trainbadger.app/register?src=agents, verifies their email, and signs in at https://trainbadger.app/login.
2. They mint a key at https://trainbadger.app/account-keys (or in the iOS app, Settings › API Access) with the scopes the agent needs. One key per agent; revocable at any time; shown once.
3. They give the key to the agent, which sends it as `Authorization: Bearer tb_live_…`.

## Rules common to both credentials

- Accepted only on the published delay-repay routes and MCP tools (see https://trainbadger.app/llms-full.txt); `/api/keys*` and `/api/account/export` require the human's login token and answer `403` to any agent credential.
- Scope errors return `403` naming the missing scope. Revoked, expired or disabled credentials return `401` (on `/mcp`, with a `WWW-Authenticate` challenge carrying `error="invalid_token"`).
- Rate limits and per-account caps are listed in https://trainbadger.app/llms-full.txt.

Contact: https://trainbadger.app/support. Terms: https://trainbadger.app/terms. Privacy: https://trainbadger.app/privacy.
