# OAuth 2.1

Fluxul prin care o aplicație externă primește acces la platformă în numele unui om, cu PKCE, acord pe firme și jetoane rotite.

## Pe scurt

Platforma este propriul ei server de autorizare. Respectă profilul OAuth 2.1 cerut de MCP:

- descoperire prin metadate (RFC 8414 și RFC 9728);
- înregistrare dinamică a clienților (RFC 7591);
- cod de autorizare cu **PKCE `S256`** obligatoriu;
- jeton de reîmprospătare **rotit** la fiecare folosire.

## Metadatele

```bash title="Descoperirea serverului"
GET /.well-known/oauth-authorization-server
GET /.well-known/oauth-protected-resource
```

Există și variantele cu sufix (`/.well-known/oauth-authorization-server/v1/oauth`, `/.well-known/oauth-protected-resource/v1/mcp`), cerute de unii clienți MCP.

## Fluxul complet

### Înregistrarea clientului

Clientul trimite `POST /v1/oauth/inregistreaza` cu `redirect_uris` și numele lui și primește un `client_id`. Sunt acceptate cel mult 5 adrese de întoarcere, fiecare de cel mult 512 caractere, și doar două feluri: adresa conectorului Claude (`https://claude.ai/api/mcp/auth_callback`) sau o adresă locală cu port explicit (`http://localhost:<port>/…`, `http://127.0.0.1:<port>/…`). Orice altă adresă primește `invalid_redirect_uri`.

### Autorizarea

Clientul deschide `GET /v1/oauth/autorizeaza` cu `response_type=code`, `client_id`, `redirect_uri`, `state`, `code_challenge` și `code_challenge_method=S256`. Parametrul `resource` e opțional; dacă îl trimiți, trebuie să fie exact valoarea `resource` din `/.well-known/oauth-protected-resource`. Orice nepotrivire oprește fluxul cu o pagină de eroare, fără redirecționare.

### Acordul

Omul ajunge pe pagina de acord a platformei, intră în cont, vede cine cere accesul și bifează firmele. Alege **Permite** sau **Refuză**.

### Codul

La permite, browserul se întoarce la `redirect_uri` cu un cod de unică folosință. La refuz, cu `error=access_denied`.

### Jetonul

Clientul schimbă codul pe jetoane la `POST /v1/oauth/jeton`.

```bash title="Schimbarea codului pe jetoane"
curl https://app.ilustromania.com/nucleu/v1/oauth/jeton \
  -d grant_type=authorization_code \
  -d code=$COD \
  -d client_id=$CLIENT_ID \
  -d redirect_uri=http://localhost:8765/callback \
  -d code_verifier=$VERIFICATOR
```

```json title="Răspuns"
{
  "access_token": "mcp_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "citire"
}
```

## Durate

| Element | Durată |
| --- | --- |
| Cererea de autorizare (pagina de acord) | 10 minute |
| Codul de autorizare | 60 de secunde, o singură folosire |
| Jetonul de acces | 1 oră |
| Jetonul de reîmprospătare | 30 de zile, rotit la fiecare folosire |

## Reguli PKCE

| Câmp | Regula |
| --- | --- |
| `code_challenge_method` | doar `S256` |
| `code_challenge` | exact 43 de caractere (SHA-256 în base64url) |
| `code_verifier` | între 43 și 128 de caractere |

## Reîmprospătare și revocare

```bash title="Jeton nou din jetonul de reîmprospătare"
curl https://app.ilustromania.com/nucleu/v1/oauth/jeton \
  -d grant_type=refresh_token \
  -d refresh_token=$REFRESH \
  -d client_id=$CLIENT_ID
```

Jetonul de reîmprospătare folosit nu mai este valid; răspunsul aduce unul nou. Pentru a opri accesul: `POST /v1/oauth/revoca`.

## Cum sunt păstrate jetoanele

Jetoanele nu se păstrează în clar: baza de date ține doar amprenta lor. Tabelele OAuth sunt scrise numai prin funcții SQL dedicate, niciodată direct de aplicație.

## Limite

| Rută | Prag | Corp maxim |
| --- | --- | --- |
| `POST /v1/oauth/inregistreaza` | 10 / oră / IP | 2048 octeți |
| `GET /v1/oauth/autorizeaza` | 60 / 10 minute / IP | — |
| `POST /v1/oauth/jeton` | 120 / 10 minute / IP | 4096 octeți |
| `POST /v1/oauth/revoca` | 120 / 10 minute / IP | 4096 octeți |
