> ## Documentation Index
> Fetch the complete documentation index at: https://docs-pos.solya.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentification

> Deux principaux acceptés : un jeton d'accès Keycloak (JWT) ou un jeton de session POS opaque. En-tête Bearer, échec fermé, et introspection via whoami.

L'API Solya POS utilise une authentification **à double acceptation**
(*dual-accept*) : une même requête est admise sur **l'un ou l'autre** de deux
types de jeton. Dans les deux cas, le jeton est présenté en en-tête
`Authorization`.

```http theme={null}
Authorization: Bearer <jeton>
```

## Les deux principaux acceptés

### 1. Jeton d'accès Keycloak (JWT)

Un bearer de forme JWT est **vérifié** par le serveur de ressources : signature,
émetteur (`issuer`), audience (`audience`) et expiration. En cas de succès, les
revendications vérifiées et un principal fondé sur les **scopes** (la
revendication `scope`) sont attachés à la requête.

* Les scopes du jeton pilotent l'autorisation par route (les permissions `pos.*`
  requises par l'opération).
* Un JWT qui échoue à la vérification est **rejeté** (échec fermé), quel que soit
  le mode d'application des scopes.

### 2. Jeton de session POS opaque

Un jeton de session opaque (hérité) est résolu contre le magasin de sessions et
donne un principal fondé sur les **rôles**. C'est le chemin de migration
transition-safe qui cohabite avec Keycloak.

Un même code d'authentification sert les deux principaux : c'est le préhandler
`authenticate` qui, selon la forme du jeton, emprunte l'un ou l'autre chemin.

## Échec d'authentification

Un jeton absent, ou un jeton qui ne satisfait aucun des deux chemins,
court-circuite la requête avec un **401** et l'enveloppe standard :

```json theme={null}
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required",
    "statusCode": 401
  }
}
```

<Note>
  Cette couche prouve **qui** est l'appelant. L'autorisation (ce qu'il a le droit
  de faire) est appliquée séparément : par les cas d'usage (RBAC de session) et
  par le garde de scope de route (scopes Keycloak).
</Note>

## Introspection : `whoami`

Pour savoir ce que l'appelant a le droit de faire **avant** d'appeler une
opération :

```http theme={null}
GET /v1/auth/whoami
```

```json theme={null}
{
  "data": {
    "effectivePermissions": ["pos.catalog.read", "pos.checkout.finalize"]
  }
}
```

`effectivePermissions` liste les scopes `pos.*` effectifs — le même vocabulaire
que les gardes de route appliquent et que les opérations annoncent sous
`x-required-permissions`. Tout acteur authentifié peut s'introspecter (aucun
scope de domaine n'est requis). Voir [Agents & MCP](/fr/developers/agent-mcp).

## Connexion locale (développement)

En développement, un stub de connexion `POST /v1/auth/login` peut émettre une
session à partir d'un `subject` connu, **sans mot de passe**. Il n'est monté que
lorsque `POS_ALLOW_STUB_AUTH=true`, et **jamais** en production (`POST
/v1/auth/login` répond `404`). Les droits sont dérivés côté serveur à partir du
seul `subject` : rien dans le corps de la requête ne peut les élargir.
