# auth.md

You are an agent. This service supports **agentic registration** for Context.dev: discover → register → **deliver setup link & code to the user** → user completes claim in browser → poll for access_token → call API.

Context.dev is **The Web Context API** — the #1 API to give AI agents web context.

## Service details

- **Service name:** Context.dev
- **Description:** The Web Context API — brand data, web scraping, enrichment, and structured web extraction for AI agents.
- **Resource:** `https://api.context.dev/`
- **Authorization server:** `https://www.context.dev`
- **Contact:** support@context.dev

### Scopes

| Scope | Description |
| ----- | ----------- |
| `api.read` | Read access to Context.dev API endpoints (brand lookup, web scrape/crawl, search, enrichment, and related read operations). |
| `api.write` | Write access for mutating API operations. Agent credentials are issued with both `api.read` and `api.write` after claim. |

### Policies and pricing

- **Pricing:** https://www.context.dev/pricing
- **Terms of service:** https://www.context.dev/terms
- **Privacy policy:** https://www.context.dev/privacy

## Supported registration method (v1)

Only `service_auth` is supported. Send the user's email as `login_hint`; the user completes signup, email verification, and claim ceremony on a service-owned page.

## Step 1 — Discover

Fetch Protected Resource Metadata:

```http
GET https://www.context.dev/.well-known/oauth-protected-resource
```

Fetch Authorization Server metadata:

```http
GET https://www.context.dev/.well-known/oauth-authorization-server
```

## Step 2 — Register (service_auth)

```http
POST https://www.context.dev/agent/identity
Content-Type: application/json

{
  "type": "service_auth",
  "login_hint": "user@example.com",
  "api_key_name": "Acme Marketing Bot"
}
```

Optional `api_key_name` (string, max 60 characters) sets the label on the API key created after claim. Defaults to `Agent` if omitted.

**Set a descriptive name when you can** — e.g. the project, app, or environment you are setting up (`"Acme Marketing Bot"`, `"Internal tool for web brand analysis"`). The user will see this name in their [API keys dashboard](https://www.context.dev/dashboard/api-keys). A clear label helps them tell this key apart from others so they do not accidentally delete or disable it later.

Response includes a `claim` block with `user_code`, `verification_uri`, `expires_in`, and `interval`. When provided, `api_key_name` is echoed in the response. **No `identity_assertion` is issued until the user completes the claim ceremony.**

Hold `claim_token` in memory for polling; do not persist it. **Do not call `/oauth2/token` until you have completed Step 3** (deliver `claim.verification_uri` and `claim.user_code` to the user).

## Step 3 — Deliver setup link and code to the user (required before polling)

**This step is mandatory and must happen before Step 5 (polling).** Immediately after a successful registration response, stop and send the user a message that includes the **full** `claim.verification_uri` and **full** `claim.user_code`. Do not summarize, omit, truncate, or replace them with placeholders like "click here". Do not start polling until this message has been sent.

The service **never emails the setup code**. If the user does not receive both values from you, setup cannot proceed.

Copy the template below every time. Replace `{verification_uri}`, `{user_code}`, and `{expires_in}` with the exact values from the registration response (`claim.verification_uri`, `claim.user_code`, `claim.expires_in`).

---

**Verify your account and complete setup in your browser**

1. Copy and open this URL in your browser:

```
{verification_uri}
```

2. Enter this 6-digit setup code:

```
{user_code}
```

3. Follow the instructions on the page to complete setup and verify your email if prompted.

---

**Important**

- This setup code expires in **{expires_in} seconds** — complete setup before it expires.
- If the code expires, tell the user and start a new setup flow.
- Once you display the above information you can start polling for the access token (Step 5).

**Do:**
- Send the template (with real values filled in) **before** your first `/oauth2/token` poll.
- Put the URL and code in separate fenced blocks or clearly labeled lines so the user can copy them.
- Wait for the user to complete signup/sign-in, enter the code, and verify email if prompted.

**Do not:**
- Skip this step or say only "check your email" / "complete setup in the browser" without the link and code.
- Start polling (Step 5) before delivering the link and code.

## Step 4 — Claim ceremony (human-only, in browser)

The user completes Step 3 in **their own browser**.

Expected user flow on the setup page:

1. Sign up (new account) or sign in (existing account) for the `login_hint` email.
2. Enter the **6-digit setup code** you provided in Step 3.
3. Verify email if prompted (password signup only).
4. Finish setup — you will receive an `access_token` on the next poll (Step 5).

While the user is working, you may acknowledge their messages but **do not poll** until Step 3 is done. After Step 3, poll in Step 5.

## Step 5 — Poll for access_token

**Prerequisite:** You have already sent the user `claim.verification_uri` and `claim.user_code` (Step 3). If not, go back and do that first — polling without the user having the link and code will always fail.

```http
POST https://www.context.dev/oauth2/token
Content-Type: application/x-www-form-urlencoded

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

While waiting:

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

If email verification is pending:

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

On success:

```json
{
  "access_token": "ctxt_secret_...",
  "token_type": "Bearer",
  "expires_in": 0,
  "scope": "api.read api.write",
  "identity_assertion": "<service-signed JWT>",
  "assertion_expires": "2026-05-04T13:00:00.000Z"
}
```

**Important:** `access_token` is a long-lived Context.dev API key (`ctxt_secret_...`). Store it and reuse it for API calls. It does **not** auto-expire.

- `expires_in: 0` means the credential is non-expiring (OAuth-shaped metadata only — not a TTL on the key).
- The key remains valid until revoked via `/oauth2/revoke`, disabled in the dashboard, or the account loses access.
- `identity_assertion` expires (~1 hour) and can be re-exchanged via jwt-bearer when configured.

Use the API key as a Bearer token:

```http
GET https://api.context.dev/v1/...
Authorization: Bearer ctxt_secret_...
```

Honor `interval` (seconds) between polls. Default: 5 seconds. Polling faster returns `slow_down`. **Only begin polling after Step 3 is complete.**

## Step 6 — Revoke (optional)

```http
POST https://www.context.dev/oauth2/revoke
Content-Type: application/x-www-form-urlencoded

token=<access_token>
```

## JWT bearer refresh

When `identity_assertion` is returned and `AGENT_AUTH_JWT_PRIVATE_KEY` is configured, exchange it for a fresh access_token:

```http
POST https://www.context.dev/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=<identity_assertion>
&resource=https://api.context.dev/
```
