
# Front API access token

The account endpoints of the front API (`/fapi/v1/me…`, `/fapi/v1/verifications…` and `/fapi/v1/organizations…`) accept two kinds of authentication:

- the user's `__Host-idp_session` cookie, on the environment's sign-in host (the hosted account pages use this);
- a front API access token, for a first-party application that runs on another origin (a single-page app or a native app).

## Getting the token

Ask the token endpoint for the front API as the resource (RFC 8707), in an authorization code exchange or a refresh:

```http
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
DPoP: <proof>

grant_type=refresh_token&refresh_token=<refresh token>&client_id=app_01j9...&resource=https%3A%2F%2Facme.keenauth.app%2Ffapi
```

The value is the environment's issuer followed by `/fapi`. The request is accepted only when:

- the application is an active first-party application (otherwise `invalid_target`);
- the request carries a DPoP proof (otherwise `invalid_dpop_proof`);
- the grant belongs to a sign-in session, and on a refresh the refresh token is DPoP-bound (otherwise `invalid_target`).

Only the access token changes. Its audience is `<issuer>/fapi`, it carries only the OpenID scopes of the grant (`openid`, `profile`, `email`, `phone`, `offline_access`), it lives as long as a userinfo token, and it is bound to the proof's key. The grant itself, its refresh token and its scopes stay as they are; a refresh rotates the refresh token as usual.

## Calling the account endpoints

Send the token with the DPoP scheme and a new proof for each request (RFC 9449):

```http
GET /fapi/v1/me/sessions
Idp-Client: <SDK identifier>
Authorization: DPoP <access token>
DPoP: <proof with htm, htu, iat, jti, ath>
```

- `htu` is `https://<sign-in host>` plus the path, without the query. `ath` is the base64url SHA-256 of the access token.
- No cookie is read. The request acts on the session the token was issued for; if that session ended, the token stops working.
- A browser request from another origin must come from one of the application's allowed web origins. Cross-origin requests never carry credentials.
- A sensitive change still needs a recent sign-in. The `401 step_up_required` answer then carries `WWW-Authenticate: DPoP error="insufficient_user_authentication", …, max_age=300`.

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `malformed_request` | More than one `Authorization` header, or one that is not `DPoP <token>`. |
| 401 | `unauthenticated` | The token is invalid, expired or revoked, has another audience, is not DPoP-bound, or its session or application is no longer active. |
| 401 | `invalid_dpop_proof` | The proof is invalid or was signed with another key. |
| 401 | `use_dpop_nonce` | Send a new proof with the nonce from the `DPoP-Nonce` header. |
| 403 | `origin_not_allowed` | The page origin is not one of the application's allowed web origins. |

Each `401` carries a `WWW-Authenticate: DPoP …` challenge.
