# Authentication and authorization

Agent Library uses OAuth 2.1, authorization code grant with PKCE. Reading the
catalogue needs no token. Anything that creates state, a cart, a mandate, an
order or a payment, needs an access token carrying the matching scope.

Issuer: https://agentlibrary.ori.lat
Metadata: https://agentlibrary.ori.lat/.well-known/oauth-authorization-server

## Scopes

| scope | grants |
| --- | --- |
| `catalog:read` | Ler catálogo, preços, estoque e prazos de entrega. |
| `cart:write` | Criar e alterar carrinhos. |
| `order:write` | Criar pedidos a partir de um carrinho. |
| `mandate:create` | Criar mandatos de pagamento com limite de valor e prazo de validade. |
| `payment:execute` | Executar cobrança dentro do limite de um mandato válido. |
| `receipt:read` | Ler recibos, rastreio e histórico de pedidos. |

Scopes are additive and are requested space separated on the authorization
request. An unknown scope is rejected rather than silently dropped.

## Endpoints

- authorization_endpoint: https://agentlibrary.ori.lat/oauth/authorize
- token_endpoint: https://agentlibrary.ori.lat/oauth/token
- revocation_endpoint: https://agentlibrary.ori.lat/oauth/revoke
- introspection_endpoint: https://agentlibrary.ori.lat/oauth/introspect

Supported: `response_type=code`, `grant_type=authorization_code`,
`grant_type=refresh_token`, `code_challenge_method=S256`.

PKCE is mandatory. A request without `code_challenge` is rejected.

## Pre-registered pilot client

    client_id:     agentlibrary-pilot-client
    redirect_uris: http://127.0.0.1:8765/callback
                   http://localhost:8765/callback
                   https://agentlibrary.ori.lat/oauth/playground

The client secret is published in the repository README, because this is a
research pilot and there is nothing behind the door worth protecting.

## Token lifetimes

- access token: 3600 seconds
- refresh token: 30 days, rotated on every use

Revoking an access token also revokes the refresh token issued alongside it.

## Payment mandates are separate from scopes

A scope says what an agent may call. A mandate says how much it may spend. Both
are required to complete a purchase, and they are deliberately distinct objects.

A mandate declares four things:

    scope        which operations the authority covers
    limit        a ceiling, in integer cents, with an explicit currency
    duration     the window in which it is valid
    revocation   the documented path to cancel it early

Create one with `POST /api/v1/mandates` holding the `mandate:create` scope.
Revoke one with `DELETE /api/v1/mandates/{id}`, the path is echoed back inside
every mandate object.

A charge above the ceiling returns `422 mandate_limit_exceeded`. A charge
against a revoked or expired mandate returns `409`. In both cases nothing is
charged. These refusals are the reason the mandate exists.

## Reporting problems

Security issues: security@agentlibrary.ori.lat
Everything else: contato@agentlibrary.ori.lat
