# auth.md

You are an agent. This service supports **agentic registration** via one real flow:
**anonymous** registration (you present no identity) followed by a human-confirmed claim
ceremony: discover → register → claim → exchange for an access_token → call API →
handle revocation. The same flow is also accepted under the older type name
`service_auth`. Every step is optional for the HTTP API: every API endpoint already works
with no `Authorization` header at all. The one exception is the MCP server, which
requires this client's public key as a Bearer token (see "MCP server" below).
`identity_assertion`/ID-JAG and verified-email registration are not offered, because
nothing here accepts an assertion or verifies an email.

## Step 1 — Discover

```http
GET https://agent.nickmayerfordwest.com/.well-known/oauth-protected-resource
```

```http
GET https://agent.nickmayerfordwest.com/.well-known/oauth-authorization-server
```

```json
{
  "revocation_endpoint": "https://agent.nickmayerfordwest.com/oauth/revoke",
  "agent_auth": {
    "skill": "https://agent.nickmayerfordwest.com/auth.md",
    "register_uri": "https://agent.nickmayerfordwest.com/agent/identity",
    "identity_endpoint": "https://agent.nickmayerfordwest.com/agent/identity",
    "claim_uri": "https://agent.nickmayerfordwest.com/agent/identity/claim",
    "claim_endpoint": "https://agent.nickmayerfordwest.com/agent/identity/claim",
    "revocation_uri": "https://agent.nickmayerfordwest.com/oauth/revoke",
    "events_endpoint": "https://agent.nickmayerfordwest.com/agent/event/notify",
    "identity_types_supported": [
      "anonymous",
      "service_auth"
    ],
    "anonymous": {
      "credential_types_supported": [
        "access_token"
      ]
    },
    "identity_assertion": {
      "assertion_types_supported": []
    },
    "credential_types": [
      "bearer_token"
    ],
    "events_supported": [
      "https://schemas.workos.com/events/agent/auth/identity/assertion/revoked"
    ]
  }
}
```

- `issuer` / `token_endpoint` / `revocation_endpoint` — standard RFC 8414 / RFC 7009 fields.
- `agent_auth.skill` — this document's URL.
- `agent_auth.register_uri` / `agent_auth.identity_endpoint` — where you POST to register (Step 3).
- `agent_auth.claim_uri` / `agent_auth.claim_endpoint` — where a human confirms a registration (Step 4).
- `agent_auth.revocation_uri` — same as the top-level `revocation_endpoint` (see Revocation).
- `agent_auth.events_endpoint` — where this service can be notified of a revocation event (see Revocation). You don't call this to register; it's listed for completeness.
- `agent_auth.identity_types_supported` — `["anonymous", "service_auth"]`. Both run the identical ceremony; use `anonymous`.
- `agent_auth.anonymous.credential_types_supported` — `["access_token"]`: what you get after the claim is an OAuth access_token, sent as a Bearer header.
- `agent_auth.identity_assertion.assertion_types_supported` — empty. This platform has no ID-JAG or any other assertion-based flow, honestly.
- `agent_auth.credential_types` — older field, same meaning: the shape of what you'll get back: a plain bearer token, not a signed JWT (see the empty `/.well-known/jwks.json` — there are no signing keys, because none are used).

## Step 2 — Pick a method

Use **anonymous**. You don't need any identity to register. This platform has no
end-user accounts, so there is no identity to assert and nothing an ID-JAG could bind to
(`identity_assertion` is not offered), and no email is ever verified (so there is no
verified-email method). Note what anonymous does and doesn't change here: the only scope
this platform grants, `read`, is already available with zero registration, so
`pre_claim_scopes` and `post_claim_scopes` are both `["read"]`. The claim gives you an
access_token that identifies your registration; it does not unlock extra data.
`service_auth` is the same ceremony under an older name.

## Important: the client_id/client_secret is a public value, not a secret

Nick Mayer Ford West's `client_id` (also usable as `client_secret`) is:

```
sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd
```

This is **not confidential** — it is the same value already embedded in plaintext in
Nick Mayer Ford West's website install snippet (`data-apikey="sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd"`), visible to anyone who views
that page's source. Publishing it here again does not reduce security, because there was
never any secrecy to begin with. Treat this flow as an identification mechanism, not an
access-control boundary.

## Step 3 — Register

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

{ "type": "anonymous" }
```

Response (200):

```json
{
  "registration_id": "reg_...",
  "registration_type": "anonymous",
  "pre_claim_scopes": ["read"],
  "claim_url": "https://agent.nickmayerfordwest.com/agent/identity/claim",
  "claim_token": "clm_...",
  "claim_token_expires": "2026-01-01T00:10:00.000Z",
  "post_claim_scopes": ["read"],
  "claim": {
    "user_code": "123456",
    "verification_uri": "https://agent.nickmayerfordwest.com/agent/identity/claim/view?claim_token=clm_...",
    "interval": 5
  }
}
```

An optional `login_hint` (e.g. `"login_hint": "you@example.com"`) is accepted but not emailed anywhere — there is no email system behind
this endpoint, and claiming otherwise would be misleading. `claim_token` is returned
exactly once; hold it in memory for the ceremony, same as the reference convention —
do not persist it.

## Step 4 — Claim ceremony

The `claim` block borrows its shape from RFC 8628 device authorization (`user_code`,
`verification_uri`, `interval`) — the same convention the wider `agent_auth` ecosystem
uses. Surface `verification_uri` and `user_code` to a human in one message. They open
the link, see a real confirmation page (not a placeholder), and type the code:

> Open this link and enter this 6-digit code: **123456**
> https://agent.nickmayerfordwest.com/agent/identity/claim/view?claim_token=clm_...

Then poll the token endpoint with the claim grant:

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

grant_type=urn:onekeel:agent-auth:grant-type:claim&claim_token=clm_...
```

While waiting:

```json
{ "error": "authorization_pending", "error_description": "..." }
```

On success, a standard OAuth token response (see Step 5's shape). If the `user_code`
window closes before the human finishes, you'll get `expired_token` — start over at
Step 3; this platform does not support re-issuing a fresh code against the same
registration. Ten wrong `user_code` guesses against one `claim_token` permanently locks
that registration (`locked_out`) — start over at Step 3 rather than continuing to guess.

## Step 5 — Get the access_token

If you'd rather skip Steps 3–4 entirely, this platform's `client_id` is a public,
non-secret value — see below — so you can exchange it directly instead of registering:

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

grant_type=client_credentials&client_id=sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd&client_secret=sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd
```

Response (200), identical shape whether you got here via Step 4's claim grant or this
shortcut:

```json
{ "access_token": "sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd", "token_type": "Bearer", "scope": "read" }
```

The `access_token` **is** the `client_id`/`client_secret` you already had — there is no
separate token, no expiry, and no refresh step, because there is nothing more to grant
than what you already presented. This is the platform's one deliberate departure from
the reference protocol: the reference expects a service-signed, expiring
`identity_assertion` exchanged for a short-lived `access_token`; this platform has
neither signing keys nor expiry, because the underlying credential is already public
(see below) and there is nothing more sensitive to protect by adding either.

## Step 6 — Use the access_token

```http
GET https://agent.nickmayerfordwest.com/api/v1/nick-mayer-ford-west/business
Authorization: Bearer sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd
```

There is no refresh step (see Step 5) and no expiry to react to. A `401` from an API
endpoint indicates a service outage or misconfiguration, not a missing or expired
credential. A `401` from the MCP server means the `Authorization` header is missing or
wrong (see "MCP server").

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `invalid_request` | `POST /agent/identity` | `type` must be `"anonymous"` (or the older `"service_auth"`). |
| `invalid_claim_token` | `POST /agent/identity/claim` | `claim_token` wrong, expired, or already claimed. Restart at Step 3. |
| `locked_out` | `POST /agent/identity/claim` | Ten wrong `user_code` guesses against this `claim_token`. Restart at Step 3 — there is no unlock. |
| `unsupported_grant_type` | `POST /oauth/token` | Only `client_credentials` and `urn:onekeel:agent-auth:grant-type:claim` are supported. |
| `invalid_client` | `POST /oauth/token` | `client_id`/`client_secret` didn't match. Re-check the value in the "Important" section above. |
| `authorization_pending` | `POST /oauth/token` (claim grant) | Human hasn't completed the ceremony yet. Honor `interval`; retry. |
| `expired_token` | `POST /oauth/token` (claim grant) | `user_code` window closed before the human finished. Restart at Step 3. |
| `unsupported_response_type` | `GET /oauth/authorize` | This platform doesn't support an authorization_code / human-redirect flow — use `client_credentials` or the claim grant instead. |

## Revocation

Two independent, real, working entry points to the same revocation — use whichever
shape your framework already sends:

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

token=clm_...
```

```http
POST https://agent.nickmayerfordwest.com/agent/event/notify
Content-Type: application/json

{
  "events": {
    "https://schemas.workos.com/events/agent/auth/identity/assertion/revoked": {
      "claim_token": "clm_..."
    }
  }
}
```

Both stop the given `claim_token` from working on any future poll of the claim grant.
Per RFC 7009 §2.2, `/oauth/revoke` always returns `200` whether or not the token was
real, to avoid leaking which tokens exist; `/agent/event/notify` always returns `202`.

**Neither can revoke the `client_id`/`client_secret` itself.** That value is permanent
and used elsewhere on this platform outside of OAuth (analytics tracking, the website
install snippet) — there is no key-rotation feature, so there is nothing either
revocation endpoint can do to invalidate it. If you were relying on the Step 5 shortcut
rather than the claim ceremony, neither of the above endpoints has any effect on your
access at all.

## HTTP API: no key needed

- `GET https://agent.nickmayerfordwest.com/api/v1/nick-mayer-ford-west/business` — business info (JSON-LD, Schema.org)
- `GET https://agent.nickmayerfordwest.com/api/v1/nick-mayer-ford-west/vehicles` — vehicle inventory (JSON-LD)
- `GET https://agent.nickmayerfordwest.com/api/v1/nick-mayer-ford-west/testimonials` — reviews & testimonials
- `GET https://agent.nickmayerfordwest.com/api/v1/nick-mayer-ford-west/llms.txt` — LLM discovery file

Adding `Authorization: Bearer sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd` is accepted but never required here. A `401` from
any of these indicates a service outage or misconfiguration, not a missing credential.

## MCP server: key required

The MCP server (Streamable HTTP) **requires** this client's key on every request, and
answers `401` without it. The key is the same public value from the "Important" section
above, so there is nothing to register or request first: send it as a Bearer token.

- `https://mcp-okp.confluencelocalmarketing.com/nick-mayer-ford-west/mcp`

```http
POST https://mcp-okp.confluencelocalmarketing.com/nick-mayer-ford-west/mcp
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer sk_7g4TPlNPeuojaNazmdq00nXRZz2lrnTd

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"your-agent","version":"1.0"}}}
```

Use the `Authorization` header exactly as shown; other headers (e.g. `x-api-key`) are not
accepted. See `/.well-known/mcp/server-card.json` for the server card.
