# auth.md – Lycée Gourdou-Leseurre

How agents obtain credentials for the site of Lycée professionnel Gourdou-Leseurre (https://www.lyceegourdouleseurre94.fr/). Accès gratuit, sans inscription ni clé API (free, no signup, no API key). L’API est en lecture seule : elle peut être appelée directement, sans bac à sable.

**Most agents need no credentials at all**: every published page, news item and API endpoint is readable anonymously. Get a token only if your client requires one, or if you are school staff needing private content.

## Discover

- Authorization server metadata (RFC 8414): `https://www.lyceegourdouleseurre94.fr/.well-known/oauth-authorization-server` – contains the `agent_auth` block (`skill`, `identity_endpoint`, `identity_types_supported`).
- Protected resource metadata (RFC 9728): `https://www.lyceegourdouleseurre94.fr/.well-known/oauth-protected-resource` – `resource`, `authorization_servers`, `scopes_supported`.
- A `401` from `https://www.lyceegourdouleseurre94.fr/mcp` carries `WWW-Authenticate: Bearer error="invalid_token", resource_metadata="…"` pointing to the protected resource metadata.

Scopes:
- `content:read` : Lire les pages et actualités publiées (accordé par défaut, sans jeton).
- `content:read_private` : Lire en plus les contenus privés et brouillons (comptes équipe uniquement, lecture seule).

## Pick a method

| `identity_types_supported` | Use when | Scope |
|---|---|---|
| `anonymous` | You are an autonomous agent with no user identity | `content:read` |
| OAuth 2.1 authorization code + PKCE | A school staff member signs in with their WordPress account | `content:read_private` |

`identity_assertion` (ID-JAG, `urn:ietf:params:oauth:token-type:id-jag`) and `service_auth` are **not** supported: this site has no user accounts for the public, so there is nothing to bind an external identity to.

## Register

Anonymous identity – no prior registration:

```
POST https://www.lyceegourdouleseurre94.fr/agent/identity
Content-Type: application/json

{"type":"anonymous"}
```

Response `201`: `{"identity_type":"anonymous","identity_assertion":"<JWT>","expires_in":600,"token_endpoint":"https://www.lyceegourdouleseurre94.fr/oauth/token","grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer"}`. The assertion is single-use and valid 10 minutes.

OAuth clients register dynamically (RFC 7591) at `https://www.lyceegourdouleseurre94.fr/oauth/register` with `{"client_name":"…","redirect_uris":["…"]}` (https, or http on localhost). Send an `Idempotency-Key` header to retry safely.

## Claim

Anonymous identities cannot be claimed by a user: there is no `claim_endpoint`. Staff who need private content use the OAuth flow instead (`https://www.lyceegourdouleseurre94.fr/oauth/authorize`, WordPress login and consent screen).

## Exchange

```
POST https://www.lyceegourdouleseurre94.fr/oauth/token
Content-Type: application/x-www-form-urlencoded

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

Response: `{"access_token":"…","token_type":"Bearer","expires_in":3600,"scope":"content:read"}`. No refresh token is issued for anonymous identities: request a new assertion when the token expires.

OAuth: `grant_type=authorization_code` with `code`, `redirect_uri`, `client_id` and `code_verifier`; then `grant_type=refresh_token` (rotating, 30 days).

## Use the access_token

Send `Authorization: Bearer <access_token>` to `https://www.lyceegourdouleseurre94.fr/mcp`, `https://www.lyceegourdouleseurre94.fr/a2a` or `https://www.lyceegourdouleseurre94.fr/wp-json/…`. Tokens are read-only; a `content:read_private` token may only `GET` content routes (`/wp/v2/posts`, `/pages`, `/media`, `/categories`, `/tags`, `/search`).

## Errors

- Token endpoint (RFC 6749): `invalid_grant` (assertion or code invalid, expired or reused), `unsupported_grant_type`, `invalid_request`.
- `/agent/identity`: `400 unsupported_identity_type` for any type other than `anonymous`.
- Resources: `401` with `WWW-Authenticate` (token invalid or expired → get a new one), `403 insufficient_scope`, `429 rate_limited` with `Retry-After`.

## Revocation

```
POST https://www.lyceegourdouleseurre94.fr/oauth/revoke
Content-Type: application/x-www-form-urlencoded

token=<access_token or refresh_token>
```

RFC 7009: always `200` (`{"revoked":true}`), even for an unknown token. Access tokens also expire after one hour.