# auth.md

## Audience

Software agents and developer tools calling the hosted MCP endpoint (`https://edwardtadros.com/mcp`) or dossier REST API (`https://edwardtadros.com/api/v1`). The `dossier:ask` scope is required for the MCP `ask_prospaero` tool and raises the REST API's per-IP tier from 20 to 120 requests per minute. Anonymous REST search and read access remains public.

## Discover

Fetch the OAuth Authorization Server Metadata, RFC 9728 Protected Resource Metadata, and public signing keys:

```http
GET https://edwardtadros.com/.well-known/oauth-authorization-server
GET https://edwardtadros.com/.well-known/oauth-protected-resource
GET https://edwardtadros.com/.well-known/jwks.json
```

The resource identifiers are `https://edwardtadros.com/mcp` and `https://edwardtadros.com/api/v1`. Bearer credentials are accepted only in the `Authorization` header.

## Register an anonymous agent

```http
POST https://edwardtadros.com/agent/auth
Content-Type: application/json

{
  "type": "anonymous",
  "requested_credential_type": "access_token",
  "agent_name": "Example Agent",
  "redirect_uris": ["http://127.0.0.1:8400/callback"]
}
```

`redirect_uris` is optional for the one-time identity-assertion exchange. HTTPS redirects and native-app loopback HTTP redirects are accepted; other plaintext HTTP redirects, fragments, credentials, wildcards, and non-canonical URLs are rejected.

The no-store response contains a public `client_id`, a one-time service-signed `identity_assertion`, a registration-scoped `claim_token`, and the claim, authorization, token, protected-resource, and revocation endpoints. Do not log or commit the assertion or claim token. Anonymous creation is limited to five registrations per source per hour and 100 globally per hour.

## Complete the browser claim

Both credential flows require the registration to be claimed. Start a claim with the registration-scoped token:

```http
POST https://edwardtadros.com/agent/auth/claim
Content-Type: application/json

{ "claim_token": "REGISTRATION_CLAIM_TOKEN" }
```

Open the returned `verification_uri` in a browser and enter its `user_code`. The code expires after ten minutes, five incorrect attempts lock the session, and successful confirmation creates a secure 30-minute same-browser consent cookie. A claimed registration can repeat this confirmation later to renew browser consent.

## Option A: one-time identity assertion

After the claim succeeds, exchange the assertion once using the JWT bearer grant:

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

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=IDENTITY_ASSERTION&scope=dossier%3Aask
```

The assertion is audience-bound to the token endpoint, expires after 15 minutes, and cannot be replayed. No refresh token is issued; use the PKCE flow when the agent needs later access tokens.

## Option B: authorization code with PKCE S256

1. Generate a fresh PKCE `code_verifier` and S256 `code_challenge`.
2. Complete or renew the browser claim and retain its consent cookie.
3. Open `https://edwardtadros.com/oauth/authorize` with `response_type=code`, the `client_id`, an exactly registered `redirect_uri`, `scope=dossier:ask`, the challenge, `code_challenge_method=S256`, and transaction-specific `state`.
4. Review and approve the scope.
5. Exchange the returned code at `https://edwardtadros.com/oauth/token` with `grant_type=authorization_code`, `client_id`, the exact `redirect_uri`, and `code_verifier`.

Authorization codes expire after five minutes, are single-use, and are bound to the client, exact redirect URI, scope, and PKCE challenge.

## Use the credential

```http
GET https://edwardtadros.com/api/v1/search?q=regulated
Authorization: Bearer ACCESS_TOKEN
```

Access tokens are ES256-signed, audience-bound to both advertised resources, limited to `dossier:ask`, and expire after ten minutes. Authenticated REST responses advertise `RateLimit-Policy: 120;w=60`; invalid, expired, or revoked credentials receive only the anonymous REST tier. The hosted MCP endpoint challenges missing or invalid credentials with the protected-resource metadata URL in `WWW-Authenticate`.

## Revoke an access token

```http
POST https://edwardtadros.com/oauth/revoke
Content-Type: application/x-www-form-urlencoded

token=ACCESS_TOKEN&token_type_hint=access_token
```

Revocation follows RFC 7009 and returns success even when the token is already invalid.

## Lifetime and privacy

- Unclaimed registrations expire after 24 hours; a claim extends a registration for at most 30 days.
- Claim sessions expire after ten minutes, consent after 30 minutes, assertions after 15 minutes, and access tokens after ten minutes.
- Claim codes, authorization codes, credentials, and rate-limit actors are stored only as hashes or one-time identifiers where persistence is required.
- Registration metadata and revocations are stored in Cloudflare D1; expired rows are removed by scheduled cleanup.
- Registration grants no private dossier or account access. The MCP and REST resources operate on Edward Tadros's public dossier.

See the [API documentation](https://edwardtadros.com/docs), [OpenAPI description](https://edwardtadros.com/openapi.json), and [privacy policy](https://edwardtadros.com/privacy).

## Contact

etadros@proticom.com
