# auth.md — nemahBites

You are an agent. nemahBites (https://nemahbites.com) is an online store in Bangladesh
selling imported chocolate, snacks and groceries. This document tells you how to
register, get an access token, and act for a customer.

**Read this first: you probably do not need a token.** The entire product
catalog is public and unauthenticated. If you only want to search products,
check a price or cite a page, stop here and use:

- `GET https://nemahbites.com/api/products?search={query}` — catalog API ([OpenAPI](https://nemahbites.com/openapi.json))
- `GET https://nemahbites.com/llms.txt` — site overview
- `curl -H "Accept: text/markdown" https://nemahbites.com/product/{slug}` — any page as Markdown

Authenticate only for **a specific customer's** orders or profile.

## Step 1 — Discover

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

The first is [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) Protected
Resource Metadata; the second is [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)
Authorization Server Metadata and carries the `agent_auth` block that points
back at this document. Any 401 from `https://nemahbites.com/api/agent/*` also carries:

```http
WWW-Authenticate: Bearer resource_metadata="https://nemahbites.com/.well-known/oauth-protected-resource"
```

Both roles are the same origin here: issuer, resource and authorization server
are all `https://nemahbites.com`.

## Step 2 — Pick a method

| You have | Use |
| --- | --- |
| Nothing — no user, no browser | [anonymous](#anonymous) |
| The customer's email address | [service_auth](#service_auth) |
| A browser you can send the customer to | [authorization code + PKCE](#authorization-code--pkce) |

nemahBites does **not** accept provider-issued ID-JAGs
(`urn:ietf:params:oauth:token-type:id-jag`): there is no trust list to verify
them against, so `type: "identity_assertion"` is rejected with
`identity_assertion_not_enabled`. Do not send one.

## Step 3 — Register

### anonymous

No user identity is asserted, so no consent gate is needed here.

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

{ "type": "anonymous", "agent_name": "Your Agent" }
```

```json
{
  "registration_id": "reg_...",
  "registration_type": "anonymous",
  "identity_assertion": "<service-signed JWT>",
  "assertion_expires": "…",
  "scopes": ["catalog.read"],
  "post_claim_scopes": ["catalog.read", "orders.read", "profile.read"],
  "claim_token": "…",
  "claim_url": "https://nemahbites.com/api/agent/identity/claim"
}
```

Keep `claim_token` — it is the only way to raise your scope later, and it is
shown once. Go to [Step 5](#step-5--exchange-the-assertion).

### service_auth

Same call with the customer's email. It does **not** grant anything on its own:
you still start at `catalog.read` and still need the claim ceremony. The email
only pre-fills the confirmation screen.

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

{ "type": "service_auth", "login_hint": "customer@example.com" }
```

Before sending this, tell the user you are about to name their email address to
nemahBites, and get their agreement. It is their only consent gate.

## Step 4 — Claim ceremony

Run this when the customer wants you to see their orders. It is what turns an
anonymous registration into one bound to a real account.

### 4a. Ask for a code

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

{ "claim_token": "…", "email": "customer@example.com" }
```

```json
{
  "claim": {
    "user_code": "482913",
    "expires_in": 600,
    "interval": 5,
    "verification_uri": "https://nemahbites.com/login?redirect=%2Fagent%2Fclaim%3F…",
    "verification_uri_complete": "https://nemahbites.com/agent/claim?claim_attempt_token=…"
  }
}
```

### 4b. Hand off to the human

Show them `verification_uri` and `user_code`, verbatim. They open the link,
sign in to their own nemahBites account, and type the six digits. No email is
sent — the account password is the proof of ownership. Never type the code for
them and never ask them to paste their password to you.

### 4c. Poll

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

grant_type=urn:nemahbites:agent-auth:grant-type:claim&claim_token=…
```

Wait `interval` seconds between attempts.

- `authorization_pending` — not done yet, keep polling.
- `expired_token` — the 10-minute code window closed. Go back to 4a for a new
  code; the `claim_token` itself is still good.
- `200` — you get an access token with `catalog.read orders.read profile.read`.

## Step 5 — Exchange the assertion

For an unclaimed registration, trade the `identity_assertion` for an access
token using the [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) JWT bearer
grant. The assertion is valid for ten minutes; the token for one hour.

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<identity_assertion>
```

```json
{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "catalog.read"
}
```

Refresh with `grant_type=refresh_token`. Refresh tokens rotate: the one you
present is dead as soon as you get a new one, so store the replacement.

## Authorization code + PKCE

Use this instead of Steps 3–5 when you can put a browser in front of the
customer. Register a client first
([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)):

```http
POST https://nemahbites.com/api/oauth/register
Content-Type: application/json

{
  "client_name": "Your Agent",
  "redirect_uris": ["https://your-agent.example/callback"],
  "token_endpoint_auth_method": "none"
}
```

Redirect URIs must be `https`, or `http` on `127.0.0.1`/`localhost`.
Use `"token_endpoint_auth_method": "none"` for anything running on the user's
machine, where a client secret cannot be kept secret.

Then send the customer to:

```
https://nemahbites.com/authorize
  ?response_type=code
  &client_id=…
  &redirect_uri=…
  &scope=catalog.read%20orders.read
  &state=…
  &code_challenge=…
  &code_challenge_method=S256
```

`S256` is the only challenge method accepted. Exchange the returned `code`
at the token endpoint with `grant_type=authorization_code` and your
`code_verifier`.

## Step 6 — Call the API

```http
GET https://nemahbites.com/api/agent/orders
Authorization: Bearer <access_token>
```

| Endpoint | Scope |
| --- | --- |
| `GET /api/agent` | `catalog.read` — index of what your token can reach |
| `GET /api/agent/catalog/products?q=` | `catalog.read` |
| `GET /api/agent/catalog/categories` | `catalog.read` |
| `GET /api/agent/orders` | `orders.read` — the claimed customer's orders |
| `GET /api/agent/profile` | `profile.read` — their name, email, phone |

`orders.read` and `profile.read` also require the token to be bound to an
account. An unclaimed anonymous token holding those scopes still gets
`insufficient_scope` — finish Step 4 first.

## Step 7 — Revocation

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

token=<access_or_refresh_token>
```

Revoking an access token retires the whole registration behind it, which is what
a customer means by "disconnect this agent". Per
[RFC 7009](https://www.rfc-editor.org/rfc/rfc7009) this answers `200` even for
a token we have never seen. Call it as soon as the customer asks you to stop, or
when you are done for good.

## Rules

- Never log, display or forward an access token, refresh token or claim token.
- Never ask the customer for their nemahBites password. You never need it.
- Quote prices with the currency (BDT, ৳) and say they can change.
- Never state that something is in stock unless `in_stock` says so.
- Cite the page URL (`https://nemahbites.com/product/{slug}`), not the API URL.
- Rate limits are per IP and per minute: 10 registrations, 20 claim requests,
  120 token requests. Back off on `429`.

## Related

- [`/developers`](https://nemahbites.com/developers) — human-readable API docs
- [`/llms.txt`](https://nemahbites.com/llms.txt) — site overview for LLMs
- [`/openapi.json`](https://nemahbites.com/openapi.json) — OpenAPI 3.1 description
- [`/.well-known/agent-card.json`](https://nemahbites.com/.well-known/agent-card.json) — A2A agent card
- [`/.well-known/agent-skills/index.json`](https://nemahbites.com/.well-known/agent-skills/index.json) — published agent skills
- [`/.well-known/api-catalog`](https://nemahbites.com/.well-known/api-catalog) — RFC 9727 linkset
