# 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](https://docs.oidc-spa.dev/integration-guides/broken-reference) (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](#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="backend-token-validation/nestjs">nestjs</a></td><td>NestJS</td></tr><tr><td><a href="backend-token-validation/trpc">trpc</a></td><td>tRPC</td></tr><tr><td><a href="backend-token-validation/hono">hono</a></td><td>TanStack Start</td></tr><tr><td><a href="backend-token-validation/express.js">express.js</a></td><td>Koa</td></tr><tr><td><a href="backend-token-validation/fastify">fastify</a></td><td>Fastify</td></tr><tr><td><a href="backend-token-validation/koa">koa</a></td><td>Hono</td></tr><tr><td><a href="tanstack-router-start/tanstack-start">tanstack-start</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="backend-token-validation/node-http">node-http</a></td></tr><tr><td><a href="backend-token-validation/deno.serve">deno.serve</a></td></tr><tr><td><a href="backend-token-validation/bun.serve">bun.serve</a></td></tr><tr><td><a href="backend-token-validation/cloudflare-workers">cloudflare-workers</a></td></tr><tr><td><a href="backend-token-validation/vercel-edge">vercel-edge</a></td></tr></tbody></table>

</details>

## WebSocket

{% content-ref url="backend-token-validation/websocket" %}
[websocket](https://docs.oidc-spa.dev/integration-guides/backend-token-validation/websocket)
{% endcontent-ref %}

## Mock Modes

{% content-ref url="backend-token-validation/mock-modes" %}
[mock-modes](https://docs.oidc-spa.dev/integration-guides/backend-token-validation/mock-modes)
{% 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>" %}
