# auth.md

You are an agent acting for a Formswrite user. The MCP server (`https://mcp.formswrite.com/mcp`) and the REST API (`https://api.formswrite.com/api/v1`) take a bearer token. Formswrite implements standard OAuth 2.1 (authorization code with PKCE), not the agent-registration profile: it has no agent identity endpoint, and `anonymous`, `service_auth` and `identity_assertion` (ID-JAG) are not accepted.

## Step 1 - Discover
A request without a token gets 401 with `WWW-Authenticate: Bearer resource_metadata="<url>"`. Fetch that document (RFC 9728):
- MCP: https://mcp.formswrite.com/.well-known/oauth-protected-resource
- REST: https://api.formswrite.com/.well-known/oauth-protected-resource

Send its `resource` as `resource` below. `authorization_servers[0]` is `https://api.formswrite.com/oauth`; its metadata (RFC 8414) is at https://api.formswrite.com/.well-known/oauth-authorization-server/oauth and https://api.formswrite.com/oauth/.well-known/oauth-authorization-server.

## Step 2 - Pick a method
1. A user is present and can open a browser: OAuth, Steps 3 to 5. Guide for people: https://formswrite.com/mcp-server
2. Unattended, for one account: an API token the owner creates at https://formswrite.com/dashboard/settings/api (acts as that user, no scopes). Go to Step 6.
3. Only the user's email, or nothing: you cannot get access. Ask the user for 1 or 2.

Scopes:
- `forms:read` - View your forms, quizzes and their questions
- `forms:write` - Create, edit, export and publish forms and quizzes in your account
- `jobs:read` - View your document conversions and their status

## Step 3 - Register
Registration is open (RFC 7591) and needs no credential.

    POST https://api.formswrite.com/oauth/reg
    Content-Type: application/json

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

201 returns your `client_id`: a client, not access.

## Step 4 - Claim
There is no claim ceremony (`claim_token`, `user_code`). The user grants access on a consent screen. Send the browser to:

    https://api.formswrite.com/oauth/auth?response_type=code&client_id=<client_id>&redirect_uri=<uri>&code_challenge=<challenge>&code_challenge_method=S256&scope=forms:read%20forms:write%20jobs:read&resource=https://mcp.formswrite.com/mcp&state=<random>

`resource` is `https://mcp.formswrite.com/mcp` or `https://api.formswrite.com` (the MCP server if omitted). PKCE is required. The user signs in and approves; your redirect URI gets `code`, `state` and `iss`, or `error=access_denied`.

## Step 5 - Exchange
Within 10 minutes:

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

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

You get `access_token` (opaque, one hour, accepted only by the resource it was requested for), `refresh_token` and `scope`. Refresh with `grant_type=refresh_token&refresh_token=<token>&client_id=<client_id>`. The refresh token is rotated: keep the new one. Sending an old one again returns `invalid_grant` and revokes the whole grant. It expires after 60 days.

## Step 6 - Use the access_token
Send `Authorization: Bearer <token>` on every request, never in the URL. MCP tools: https://mcp.formswrite.com/.well-known/mcp/server-card.json. REST operations: https://formswrite.com/openapi.json.

## Errors
Resource servers answer JSON with `code` and `message`; the authorization server uses `error` and `error_description`.
- 401, no `error` in `WWW-Authenticate`: no credential, or an invalid API token. Step 1.
- 401 `error="invalid_token"`: expired, revoked, or issued for the other resource. Refresh; if that fails, Step 4.
- 403 `insufficient_scope`: ask the user for the scope the header names (Step 4).
- 402 `payment_required`: the plan does not include it. Tell the user.
- 422 `validation_failed`: `errors` names the field. 405: see `Allow`.
- 429 `rate_limited`: wait `Retry-After` seconds. 5xx: retry with backoff.
- Token endpoint `invalid_grant`: code or refresh token expired, used or revoked (Step 4). `invalid_client`: register again.
- Redirect with `invalid_request` or `invalid_target`: PKCE missing, or unknown `resource`.

## Revocation
RFC 7009, your own tokens only, effective at once:

    POST https://api.formswrite.com/oauth/token/revocation
    Content-Type: application/x-www-form-urlencoded

    token=<token>&client_id=<client_id>

A user can disconnect your application in the dashboard; you then get 401 `invalid_token`, and `invalid_grant` on refresh. API tokens are not revocable here; each expires after 365 days.

## agent_auth
As in the authorization-server metadata:
- register_uri: https://api.formswrite.com/oauth/reg
- revocation_endpoint: https://api.formswrite.com/oauth/token/revocation
- identity_types_supported: none
