# Shield AI — Agent Registration (auth.md)

> Public contract for agents that need to act on a Shield AI account.
> Pair this prose with the machine-readable metadata at
> [`/.well-known/oauth-authorization-server`](https://www.shieldai.me/.well-known/oauth-authorization-server)
> and [`/.well-known/oauth-protected-resource`](https://www.shieldai.me/.well-known/oauth-protected-resource).

Shield AI publishes two registration flows. Agents pick whichever their
identity provider supports. The user-claimed flow works for every agent;
the agent-verified flow trades a one-time email confirmation for an
attested ID-JAG from a trusted identity provider.

---

## Discovery

Discovery is two hops:

1. Fetch [`https://www.shieldai.me/.well-known/oauth-protected-resource`](https://www.shieldai.me/.well-known/oauth-protected-resource)
   to confirm this resource and its authorization server.
2. Fetch [`https://www.shieldai.me/.well-known/oauth-authorization-server`](https://www.shieldai.me/.well-known/oauth-authorization-server)
   to read the OAuth metadata. The `agent_auth` block on that document
   carries:

   - `flows_supported`: which of `user_claimed` and `agent_verified` are live
   - `endpoints`: the four agent endpoints (register, claim, claim_complete, revoke)
   - `identity_types_supported`: what kinds of identity proofs the resource accepts
   - `scopes_supported`: the agent-scope vocabulary
   - `rate_limits`: per-email / per-credential limits agents must respect

On any 401 from a protected endpoint we return
`WWW-Authenticate: Bearer resource_metadata="https://www.shieldai.me/.well-known/oauth-protected-resource"`
so agents can bootstrap discovery without reading documentation first.

---

## Flows supported

### User-claimed (`user_claimed`) — *live*

Agent registers with the service and the user binds it by reading a one-time code from email. No agent-provider participation required.

### Agent-verified (`agent_verified`) — *planned*

Agent's identity provider attests to the user via an audience-bound ID-JAG. Registration completes synchronously, no OTP. Trusted-provider list publishes when this lands.

---

## Endpoints

| Purpose | Method | URL |
|---|---|---|
| Register (agent-verified) | `POST` | `https://www.shieldai.me/api/agent/auth` |
| Claim (user-claimed, start) | `POST` | `https://www.shieldai.me/api/agent/auth/claim` |
| Claim complete (OTP) | `POST` | `https://www.shieldai.me/api/agent/auth/claim/complete` |
| Revoke | `POST` | `https://www.shieldai.me/api/agent/auth/revoke` |

Errors follow [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) Problem
Details (`Content-Type: application/problem+json`). Success responses
follow Shield AI's `{ "success": true, "data": { ... } }` envelope.

---

## User-claimed flow (live)

### 1. Register

```http
POST https://www.shieldai.me/api/agent/auth/claim
Content-Type: application/json

{
  "contact_email": "user@example.com",
  "agent_label": "My Personal Research Agent",
  "agent_provider": "anthropic",
  "requested_scopes": ["tools:list", "creator:read"]
}
```

**Response:**

```json
{
  "success": true,
  "data": {
    "credential_prefix": "sa_agent_<8 chars>",
    "credential": "sa_agent_<...>.<secret>",
    "claim_required": true,
    "claim_complete_uri": "https://www.shieldai.me/api/agent/auth/claim/complete",
    "expires_in_seconds": 600,
    "granted_scopes": ["tools:list"],
    "email_delivered": true
  }
}
```

- The `credential` is returned **once**. Store it securely.
- The pre-claim credential is immediately usable against tools that accept
  the granted pre-claim scope (default `tools:list`). Scope upgrades land
  when the OTP ceremony completes.
- `email_delivered: false` means the OTP email could not be sent. The
  user should be told to contact the operator instead of retrying.

### 2. Verify

```http
POST https://www.shieldai.me/api/agent/auth/claim/complete
Content-Type: application/json

{
  "credential": "sa_agent_<...>.<secret>",
  "code": "AB23-CDEF"
}
```

**Response on success:**

```json
{
  "success": true,
  "data": {
    "credential_prefix": "sa_agent_<8 chars>",
    "status": "active",
    "scopes": ["tools:list", "creator:read"],
    "claimed_at": "2026-05-26T12:00:00Z"
  }
}
```

- Codes are 8 characters from a confusables-free alphabet (no `0`/`O`/`1`/`I`).
- Hyphens and lowercase are normalized away before verification.
- Up to 5 wrong attempts
  per code; after that the agent must call `/claim` again.
- TTL is 600 seconds (10 minutes).
- Re-submitting the same valid code on an already-active credential
  returns `{ status: "active", already_claimed: true }` (idempotent).

### 3. Revoke

```http
POST https://www.shieldai.me/api/agent/auth/revoke
Content-Type: application/json

{
  "credential": "sa_agent_<...>.<secret>",
  "reason": "device_lost"
}
```

Revocation is one-way and is mirrored to the immutable
`agent_claim_attempts` audit ledger. After revocation the credential
returns `401 Invalid credential` on every other endpoint.

---

## Agent-verified flow (planned)

The endpoint at `https://www.shieldai.me/api/agent/auth` exists and accepts
`POST` requests so compliant clients can probe its shape. Until the
trusted-provider list (`trusted_providers` in the agent_auth block) is
operator-confirmed, the endpoint returns RFC 9457
`not_implemented` with a `Link` header pointing back to this document
and to the OAuth authorization-server metadata. Agents that discover this
state should fall back to the user-claimed flow above.

When this flow lands, the request shape will be:

```http
POST https://www.shieldai.me/api/agent/auth
Content-Type: application/json

{
  "id_jag": "<JWT issued by a trusted provider, audience = Shield AI>",
  "requested_scopes": ["creator:read", "threats:read"]
}
```

The response will issue an access token bound to `(iss, sub, aud)` that
the provider can revoke at any time by POSTing a logout token to
`https://www.shieldai.me/api/agent/auth/revoke`. No refresh tokens — the agent
re-presents a fresh ID-JAG to extend access.

---

## Rate limits

| Limit | Value |
|---|---|
| Claim starts per email per hour | 5 |
| Verify attempts per credential | 5 |
| OTP TTL | 600 seconds |

Exceeding any of these returns RFC 9457
`rate_limited` (HTTP 429) with a `Retry-After` header.

---

## Auditability

Every `claim_started`, `otp_sent`, `otp_verified`, `otp_failed`,
`revoked`, and `expired` event is INSERT-only in the
`agent_claim_attempts` ledger. Operators can inspect the history of a
credential at any time. UPDATE and DELETE on the ledger are rejected at
the row-level security layer.

---

## Where this maps in the OAuth specs

- [RFC 9728 — OAuth 2.0 Protected Resource Metadata](https://www.rfc-editor.org/rfc/rfc9728): served at `/.well-known/oauth-protected-resource`
- [RFC 8414 — OAuth 2.0 Authorization Server Metadata](https://www.rfc-editor.org/rfc/rfc8414): served at `/.well-known/oauth-authorization-server` (the `agent_auth` block is a Shield AI extension)
- [RFC 8252 — OAuth 2.0 for Native Apps](https://www.rfc-editor.org/rfc/rfc8252): same PKCE requirements apply to agents
- [RFC 9457 — Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457): error response shape
- [RFC 8594 — Sunset HTTP Header Field](https://www.rfc-editor.org/rfc/rfc8594) and [RFC 9745 — Deprecation HTTP Header](https://www.rfc-editor.org/rfc/rfc9745): used for endpoint sunset announcements
