# AIXO agent authentication

This document is the WorkOS-style `auth.md` skill for AIXO. Agents that need to call the studio API should follow Discover → Pick a method → Register → Claim → Exchange → Use the access_token. Protected API hosts advertise `WWW-Authenticate: Bearer resource_metadata=...` and RFC 9728 metadata at `/.well-known/oauth-protected-resource`. Keywords: agent_auth, identity_endpoint, identity_assertion, service_auth, id-jag, WWW-Authenticate.

## Discover

Fetch RFC 9728 protected-resource metadata:

- `GET https://aixo.it/.well-known/oauth-protected-resource`

The `resource` field is `https://aixo.it/api`. `authorization_servers` lists `https://aixo.it`.

Fetch RFC 8414 authorization-server metadata:

- `GET https://aixo.it/.well-known/oauth-authorization-server`

Read the `agent_auth` block: `identity_endpoint`, `claim_endpoint`, `events_endpoint`, `identity_types_supported`, and `identity_assertion.assertion_types_supported` (includes `urn:ietf:params:oauth:token-type:id-jag`). The `agent_auth.skill` URL is this file.

A 401 from `https://aixo.it/api` includes `WWW-Authenticate: Bearer resource_metadata="https://aixo.it/.well-known/oauth-protected-resource"`.

## Pick a method

`identity_types_supported`:

- `anonymous` — public catalog, markdown, NLWeb `/ask`, product MCP tools that do not mutate studio data
- `service_auth` — machine-to-machine with an issued API token (Sanctum bearer)
- `identity_assertion` — present an assertion; supported assertion type is `urn:ietf:params:oauth:token-type:id-jag`

Most agents only need `anonymous` for pricing/docs/contact. Use `service_auth` for `/api/progenia/*` and dashboard routes.

## Register

Human or agent registration:

- `POST https://aixo.it/api/register`
- JSON: `name`, `email`, `password` (min 8 chars)
- Response: `{ user, token }` — the `token` is a Sanctum personal access token

AS `registration_endpoint` is `https://aixo.it/api/register`.

## Claim

Call the advertised claim endpoint so the authorization server can bind an identity to a pending agent session:

- `POST https://aixo.it/agent/claim`
- Optional JSON: `client_name`, `redirect_uri`, `identity_type`

Anonymous callers receive a claim ticket. Authenticated callers send `Authorization: Bearer <token>`.

Identity probe (OPTIONS must not 404):

- `GET|OPTIONS https://aixo.it/agent/identity`
- `GET|OPTIONS https://aixo.it/agent/auth`

## Exchange

RFC 8414 `token_endpoint`:

- `POST https://aixo.it/oauth/token`
- JSON or form: `grant_type=password`, `username` (email), `password`
- Response: `{ access_token, token_type: Bearer, expires_in, user }`

SPA equivalent (same Sanctum token):

- `POST https://aixo.it/api/login`
- JSON: `email`, `password`, optional `remember`
- Response: `{ user, token }`

`authorization_endpoint` `GET https://aixo.it/oauth/authorize` returns discovery JSON (no browser consent UI yet). `userinfo_endpoint` `GET https://aixo.it/oauth/userinfo` requires the bearer.

## Use the access_token

Send `Authorization: Bearer <token>` on `https://aixo.it/api/*`. Example: `GET https://aixo.it/api/user`. Progenia chat requires this bearer. Public GETs (`/api/projects`, `/api/posts`) do not.

## Errors

- `401` + `WWW-Authenticate` — missing/invalid bearer; load `resource_metadata` and this skill
- `403` — authenticated but not admin for `/api/admin/*`
- `422` — validation error on register/login/contact
- `429` — contact/newsletter throttle

## Revocation

- `POST https://aixo.it/api/logout` with `Authorization: Bearer <token>`
- AS `revocation_endpoint` is the same URL
- Events (token revocation, identity changes): `GET|POST https://aixo.it/agent/events`
