npm package: @kiavi/kiavi-js
Description: Server-side verification for APIs behind a Kiavi-hosted auth server. Use this to check that an incoming request is from a real, signed-in user.
Latest version: 0.2.0

# @kiavi/kiavi-js

Server-side verification for APIs behind a Kiavi-hosted auth server. Use this to check that
an incoming request is from a real, signed-in user.

## Install

```bash
pnpm add @kiavi/kiavi-js
```

## Usage

```ts
import { KiaviClient } from '@kiavi/kiavi-js'

const kiavi = new KiaviClient('https://auth.your-domain.com', 'my-api-audience')

// In your request handler:
const token = request.headers.get('authorization')?.replace(/^Bearer /i, '') ?? ''
const user = await kiavi.verifyToken(token)

if (!user) {
  return new Response('Unauthorized', { status: 401 })
}

// user.sub, user.email, user.app_id, etc. are now trusted.
```

That's it for the common case. Instantiate once, call `verifyToken()` on every request.

## When to use `verifyToken` vs `verifySession`

`verifyToken()` is the default. It's offline (no network hop after the initial JWKS fetch),
checks signature / issuer / audience / expiration, and is what you want for essentially every
normal request.

What it **doesn't** know is anything that changed on the server after the token was issued:
a revoked session, a disabled user, a password reset. Those only become visible to
`verifyToken` when the current token expires and the client has to mint a new one — so the
staleness window is roughly "one access-token lifetime."

Kiavi currently issues access tokens with a **5-minute lifetime**, so in practice the worst
case is that a revoked session stays usable for up to 5 minutes after revocation. If that's
acceptable for the operation you're protecting, stick with `verifyToken()`. If it isn't, use
`verifySession()` below.

`verifySession()` round-trips to the auth server on every call, so it always sees live state.
Reach for it when that staleness window is unacceptable for the operation at hand — e.g.
high-value money movement, permissions changes, anything where a just-revoked session
absolutely must not go through. The cost is an extra network hop per request and a hard
dependency on the auth server being reachable.

Rule of thumb: use `verifyToken()` everywhere; use `verifySession()` only on the handful of
endpoints where the trade-off is worth it, and cache its result for the duration of the
request if you call it more than once.

## Options

```ts
new KiaviClient('https://auth.your-domain.com', 'my-api-audience', {
  debug: true, // logs to console.debug; pass a function to route logs elsewhere
})
```

## Reference

### Methods

- `verifyToken(token)` — verifies a JWT from an `Authorization: Bearer` header. Returns the
  payload (`sub`, `email`, `app_id`, etc.) on success or `null` on any failure. Never throws.
- `verifySession(cookieHeader)` — for routes that receive a session cookie directly instead
  of a bearer token. Forwards the cookie to the auth server and returns the resolved session,
  or `null` if it's missing/invalid. Never throws. Unlike `verifyToken()`, this makes a
  network round-trip on every call — cache the result per request if you use it.

### Types

`KiaviClient`, `JWTPayload`, `SessionPayload`, `KiaviClientOptions`, `KiaviTokenError`,
`KiaviDebugLogger`.

### How it works (if you're curious)

`verifyToken()` fetches the auth server's JWKS from `${authBaseUrl}/api/auth/jwks` on first
use, then verifies tokens offline against the cached keys (rotation is handled automatically
by [`jose`](https://github.com/panva/jose)). It checks signature, `iss`, `aud`, and
expiration — so a bad token is a null return, not an exception to catch.

`verifySession()` exists for the less common case where your service sees the session cookie
directly (same-site rendered routes). It just forwards the cookie header to
`${authBaseUrl}/api/auth/get-session` and trusts the auth server's response.

The browser-side counterpart that obtains these tokens in the first place is
[`@kiavi/kiavi-browser`](../kiavi-browser/README.md).