> ## 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.

# Authentication

> Authenticate every request with a bearer token — a Keycloak access token or an opaque POS session token.

Every protected operation requires a bearer token in the `Authorization` header:

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

## Two accepted token types

The backend's authentication seam is **dual-accept**: the single `bearerAuth`
security scheme accepts either kind of token, and both resolve to the same `pos.*`
scope vocabulary that the route guards enforce.

| Token type                   | Where scopes come from                                   | Typical caller                                     |
| ---------------------------- | -------------------------------------------------------- | -------------------------------------------------- |
| **Keycloak access token**    | The `scope` claim carried in the token (scopes-in-token) | An integration or agent federated through Keycloak |
| **Opaque POS session token** | Derived server-side from the session                     | The Caisse / Centrale apps after login             |

Because both populate the same request context, one code path serves either
principal — you do not pick a different endpoint or header depending on the token
type.

## The security scheme in the spec

The spec declares one HTTP bearer scheme (`bearerAuth`, `bearerFormat: JWT`). Every
authenticated operation carries the bearer security requirement; only genuinely
public routes (for example `GET /health` and the `/v1` descriptor) omit it.

## What you may do: whoami

To discover the scopes your token actually holds — before calling anything — request
your effective permissions:

```http theme={null}
GET /v1/auth/whoami
Authorization: Bearer <token>
```

It returns your effective `pos.*` scopes, the same vocabulary the per-route guards
enforce and the documented operations advertise as `x-required-permissions`:

```json theme={null}
{ "data": { "effectivePermissions": ["pos.sale.create", "pos.report.view"] } }
```

<Note>
  `whoami` is one of the few routes with a `{ data: … }` wrapper. The domain routes
  return their payload as raw JSON — see [Making requests](/en/developers/making-requests).
</Note>

## Scopes and authorization

A protected operation is gated on a `pos.*` scope. If your token is valid but lacks
the required scope, the request is rejected with `403 FORBIDDEN`; a missing or invalid
token is `401 UNAUTHORIZED`. See [Error codes](/en/developers/error-codes) for the
full map and [Agent & MCP](/en/developers/agent-mcp) for how required permissions are
advertised per operation.
