# Backend Token Validation

Now that you’ve set up oidc-spa in your web app, you can call your API like this:

```typescript
const todos = fetch("/api/todos", { 
    headers: {
        Authorization: `Bearer ${await oidc.getAccessToken()}`
    }
});
```

Next, let’s implement the backend side of things.

When you implement the server `GET /api/todos` handler, you want to read the `Authorization` header.\
Use it to authenticate the user.\
Optionally, check permissions (roles/scopes) to authorize the request.

If you’re building a JavaScript backend (Express, Hono, tRPC, NestJS, etc.), oidc-spa provides utilities to validate and decode access tokens.\
Validation includes DPoP proof checks and replay protection.

<details>

<summary>More context</summary>

The server-side validation utilities in oidc-spa implement [RFC 9068: JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens](https://datatracker.ietf.org/doc/rfc9068/).

JWT validation works offline.\
There’s no need to contact your authorization server for every request.

`oidc-spa/server` fetches the public key published by your IdP once.\
It then uses it to verify that each incoming token:

* was signed by the IdP
* targets the expected audience
* hasn’t expired
* has a valid [DPoP proof](broken://spaces/UhNOMoIddws1XoAnT5Nn/pages/AkX224WAW7UAYAoUymBd) (if applicable)

This is a big win for edge runtimes.\
Identity and authorization can be established locally, with no external round trips.

To authorize certain routes or actions, you can perform additional checks on claims like `groups` or `realm_access.roles`.

Some IdPs don’t issue JWT access tokens by default and issue opaque access tokens instead.

Opaque access tokens can’t be validated in a provider-agnostic way like JWTs can.\
If your IdP issues opaque access tokens, you’ll need provider-specific tooling.\
In that case, you won’t be able to use `oidc-spa/server`.

</details>

## Integration

Integration instruction for common HTTP framworks. This only covers REST APIs and RPC. For securing WebSocket connection [see bellow](/integration-guides/backend-token-validation.md#websocket).

<table data-view="cards"><thead><tr><th data-card-target data-type="content-ref">Docs</th><th data-hidden>Option</th></tr></thead><tbody><tr><td><a href="/pages/U95czlJaG5VGFSiLaeUm">/pages/U95czlJaG5VGFSiLaeUm</a></td><td>NestJS</td></tr><tr><td><a href="/pages/jPYFd1DOgIwEr8YEhJ6x">/pages/jPYFd1DOgIwEr8YEhJ6x</a></td><td>tRPC</td></tr><tr><td><a href="/pages/ouqjtmsZVJywv5rMklIc">/pages/ouqjtmsZVJywv5rMklIc</a></td><td>TanStack Start</td></tr><tr><td><a href="/pages/D7oSdXrY8PpRNgwq8P0j">/pages/D7oSdXrY8PpRNgwq8P0j</a></td><td>Koa</td></tr><tr><td><a href="/pages/dpMjSkwHciZoaYThM3xK">/pages/dpMjSkwHciZoaYThM3xK</a></td><td>Fastify</td></tr><tr><td><a href="/pages/YklVLJe1CNTx5tAEmDL3">/pages/YklVLJe1CNTx5tAEmDL3</a></td><td>Hono</td></tr><tr><td><a href="/pages/d1qbPv2oiUEBc6J3j5sN">/pages/d1qbPv2oiUEBc6J3j5sN</a></td><td>Express.js</td></tr></tbody></table>

<details>

<summary>JS Runtime level integration</summary>

<table data-view="cards"><thead><tr><th data-card-target data-type="content-ref">Docs</th></tr></thead><tbody><tr><td><a href="/pages/8IHPKsisQp1ZEgtiRmTI">/pages/8IHPKsisQp1ZEgtiRmTI</a></td></tr><tr><td><a href="/pages/d2qm0aCjKdy6kvR8uvUR">/pages/d2qm0aCjKdy6kvR8uvUR</a></td></tr><tr><td><a href="/pages/vNMUshRNFWPeFKiFOtWv">/pages/vNMUshRNFWPeFKiFOtWv</a></td></tr><tr><td><a href="/pages/7iLCD0QZbmGywDt5qJd7">/pages/7iLCD0QZbmGywDt5qJd7</a></td></tr><tr><td><a href="/pages/yfnnatrSBynS6LZmpmMQ">/pages/yfnnatrSBynS6LZmpmMQ</a></td></tr></tbody></table>

</details>

## WebSocket

{% content-ref url="/pages/2h9b2JIP2ifZUPpmy07q" %}
[WebSocket](/integration-guides/backend-token-validation/websocket.md)
{% endcontent-ref %}

## Mock Modes

{% content-ref url="/pages/5zhvDapwmFpSUUyRfzBp" %}
[Mock Modes](/integration-guides/backend-token-validation/mock-modes.md)
{% endcontent-ref %}

## TODO List Example

A TODO list example app built with Vite / React / TanStack Router on the frontend, and Node.js / Hono on the backend.

{% embed url="<https://youtu.be/33VijFArY9s>" %}

The app is live here:

{% embed url="<https://vite-insee-starter.demo-domain.ovh/>" %}

Source code (REST API):

{% embed url="<https://github.com/InseeFrLab/todo-rest-api>" %}

Source code (frontend):

{% embed url="<https://github.com/InseeFrLab/vite-insee-starter>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.oidc-spa.dev/integration-guides/backend-token-validation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
