# auth.md

You are an agent. The read surface of the MCP server (search, registry, listing lookups) works **anonymously — no token needed**, so you only need credentials for account-bound tools (saved searches, geocoding, listing authoring). When you do, registration is fully self-serve over standard OAuth 2.0 and there is no API key to request. Follow the steps in order.

## Step 1 — Discover

- Protected Resource Metadata (RFC 9728): https://sitemaps.spacexploration.com/.well-known/oauth-protected-resource
- Authorization Server metadata (RFC 8414): https://sitemaps.spacexploration.com/.well-known/oauth-authorization-server

The Authorization Server metadata carries an `agent_auth` block: `skill` points back to this document, `register_uri` is the client-registration endpoint, and `identity_types_supported` lists both registration methods below.

## Step 2 — Pick a method

1. **You can open a browser for the user** (desktop app, IDE, anything with a redirect) → [Method A](#method-a--browser-oauth-authorization-code--pkce). The user signs in and consents on a page you redirect them to.
2. **You are headless but know the email address of the user you act for** → [Method B](#method-b--verified-email-claim-ceremony). You relay a six-digit code to the user; they confirm it while signed in; you poll for tokens.

Anonymous registration and ID-JAG identity assertions are **not** offered — `POST https://sitemaps.spacexploration.com/agent/identity` answers `anonymous_not_enabled` / `issuer_not_enabled` if you try.

Both methods start the same way: register a client.

## Step 3 — Register a client (both methods)

```http
POST https://sitemaps.spacexploration.com/oauth/register
Content-Type: application/json

{ "client_name": "<your agent's name>", "redirect_uris": ["https://your-app.example/callback"] }
```

Redirect URIs must be HTTPS on an allow-listed host (this origin, or `https://claude.ai` for Claude's hosted callback) or use an allow-listed private-use scheme (`claude`, `cursor`, `vscode`). Method B never uses the redirect, but the field is required — your own homepage URL is fine. The response returns a **public** client — `token_endpoint_auth_method` is `none`, there is no `client_secret`. Persist the `client_id`; the registration does not expire.

## Method A — Browser OAuth (Authorization Code + PKCE)

### A1. Authorize (user interaction required)

Generate a PKCE code verifier and its S256 challenge, then send the user to:

```http
GET https://sitemaps.spacexploration.com/oauth/authorize?response_type=code&client_id=<client_id>&redirect_uri=<redirect_uri>&scope=mcp%3Ause&state=<state>&code_challenge=<challenge>&code_challenge_method=S256
```

The user signs in (or creates an account), reviews the consent screen naming your client, and approves. The authorization code arrives at your `redirect_uri`.

### A2. Exchange the code

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

grant_type=authorization_code&client_id=<client_id>&redirect_uri=<redirect_uri>&code=<code>&code_verifier=<verifier>
```

Response: a Bearer `access_token` (expires in 1 hour), a `refresh_token` (expires in 60 days), and `scope` `mcp:use`. Skip to [Use the access_token](#use-the-access_token).

## Method B — Verified-email claim ceremony

No browser redirect — you supply the user's email, hand them a code, and poll. The service sends no email and creates no account; the human proves control of the address by signing in (or signing up and verifying) and typing the code you gave them.

### B1. Register the identity

```http
POST https://sitemaps.spacexploration.com/agent/identity
Content-Type: application/json

{ "type": "identity_assertion", "assertion_type": "verified_email", "assertion": "user@example.com" }
```

Response (200):

```json
{
  "registration_id": "reg_...",
  "registration_type": "email-verification",
  "claim_url": "https://sitemaps.spacexploration.com/agent/identity/claim",
  "claim_token": "clm_...",
  "claim_token_expires": "<ISO 8601, 24h out>",
  "post_claim_scopes": ["mcp:use"],
  "claim": {
    "user_code": "123456",
    "expires_in": 600,
    "verification_uri": "<claim page URL>",
    "interval": 5
  }
}
```

`claim_token` is returned exactly once — hold it in memory for the ceremony and never surface it to anyone; it is what your polls exchange for tokens.

### B2. Hand off to the user

Surface `verification_uri` and `user_code` to the user in a single message. Suggested copy:

> Open this link, sign in (or sign up), and enter this 6-digit code: **123456**
> <verification_uri>

Be explicit that the code goes into the page they land on — not back to you. New users sign up and verify their email first; only the signed-in, verified owner of the asserted address can complete the claim.

### B3. Poll the token endpoint

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

grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=<clm_...>&client_id=<client_id>
```

Honor `interval` (5s) between polls; on `slow_down`, add 5 seconds. While the user has not finished you get `authorization_pending`. On success you get a standard token response — `access_token` (1 hour), `refresh_token` (60 days), `scope` `mcp:use` — and the `claim_token` is spent.

If the code's 600-second window lapses (`expired_token` with hint `user_code`), mint a fresh code without starting over:

```http
POST https://sitemaps.spacexploration.com/agent/identity/claim
Content-Type: application/json

{ "claim_token": "clm_...", "email": "user@example.com" }
```

The response carries a new `claim_attempt` block (same shape as `claim`) — hand the new code and link to the user and resume polling. The email must match the one you registered; the binding is immutable. If this endpoint answers `410 claim_expired`, the outer 24-hour window closed — restart at B1.

## Use the access_token

```http
POST https://sitemaps.spacexploration.com/mcp
Authorization: Bearer <access_token>
```

https://sitemaps.spacexploration.com/mcp is a Streamable-HTTP MCP endpoint. Read tools (search, registry, listing lookups) work with or without a token; the single `mcp:use` scope covers everything else. Saved searches and geocoding require any authenticated token; listing-authoring tools additionally require the user's email address to be verified, and listing mutations are gated on the user owning the listing. Rate limit: 60 requests/minute per token (or per IP when anonymous; anonymous `interpret_search` is capped at 6/minute) — on 429, back off.

Tool catalog and recommended flows: https://sitemaps.spacexploration.com/llms.txt

**Refresh.** When the access_token expires:

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

grant_type=refresh_token&refresh_token=<refresh_token>&client_id=<client_id>
```

When the refresh fails with `invalid_grant` (refresh token expired or revoked), re-run your method from the start — the client registration from Step 3 stays valid. There is no identity-assertion re-exchange in this profile; the refresh token is the long-lived credential.

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `invalid_redirect_uri` (400) | register | The redirect URI is not allow-listed. Use an allowed host or private-use scheme from Step 3. |
| `invalid_client_metadata` (400) | register | Body shape problem. Send `client_name` and a non-empty `redirect_uris` array. |
| `anonymous_not_enabled` (400) | identity | Anonymous registration is off. Use Method A or B. |
| `issuer_not_enabled` (400) | identity | No ID-JAG provider trust list. Use `assertion_type` `verified_email`. |
| `invalid_request` (400) | identity / claim | Body shape problem — the error description names the field. |
| `invalid_claim_token` (400) | claim | Unknown `claim_token`. Restart at B1. |
| `claimed_or_in_flight` (400) | claim | Already claimed — poll the token endpoint instead. |
| `claim_expired` (410) | claim | The registration's outer window closed. Restart at B1. |
| `authorization_pending` (400) | token (claim grant) | User has not finished. Keep polling on `interval`. |
| `slow_down` (400) | token (claim grant) | Polling too fast. Add 5s to your interval. |
| `expired_token` (400) | token (claim grant) | Hint `user_code`: re-initiate at the claim endpoint for a fresh code. Hint `claim_token`: restart at B1. |
| `invalid_grant` (400) | token | Code, refresh token, or claim token expired, revoked, spent, or PKCE mismatch. Re-run your method. |
| 401 | MCP endpoint | Presented token is invalid or expired. Refresh; if that fails, re-run your method. |
| Unverified-email tool error | MCP authoring tools | Ask the user to verify their email address (sent at sign-up), then retry. |
| 429 | any | Polling or calling too fast. Back off and retry. |