Kyndex
API Conventions

Authentication

How tokens work, what's public, and what to expect when things go wrong.

Endpoint schemas and parameter details live in the API Reference. This page covers authentication behavior — how tokens work, what's public, and what to expect when things go wrong.

Token Transport

All protected endpoints require an access token. Two transports are supported:

Authorization header (programmatic clients):

Authorization: Bearer <access_token>

Session cookie (browser clients):

Cookie: session=<access_token>

The Bearer prefix is case-sensitive — bearer is not accepted. The token is a base64-encoded 32-byte value issued by the login flow. It is opaque — do not parse or decode it. When both are present, the Authorization header takes precedence.

Where To Get a Token

Tokens are issued at the end of the OPAQUE login handshake:

  1. POST /auth/opaque/authenticate-start — send blinded login request
  2. POST /auth/opaque/authenticate-finish — complete handshake, receive access_token and refresh_token

The access token expires after 15 minutes. When it expires, refresh it — do not re-authenticate.

Token Refresh

POST /auth/tokens/refresh exchanges a valid refresh token for a new access token and a new refresh token. This endpoint requires no Authorization header — the caller's access token may already be expired.

Browser clients send the refresh token automatically via the kyndex_rt HttpOnly cookie. Programmatic clients pass it in the request body.

The old refresh token is permanently revoked on each call. If an attacker replays a stolen refresh token after the legitimate client has already rotated, the server rejects the stale token with 401.

For the full OPAQUE registration and login protocol, see the Authentication reference under Getting Started.

Token Expiry: 401 vs. 403

StatusMeaningWhat to do
401 UnauthorizedToken is missing, malformed, expired, or revokedRefresh the token via /auth/tokens/refresh. If that also fails, re-authenticate.
403 ForbiddenToken is valid, but you lack permission for this resourceCheck your role (e.g. entity admin vs. member). This is not a token problem.

The API will never return 401 for a permission issue or 403 for an expired token. The distinction is strict.

Public (Unauthenticated) Endpoints

The API enforces deny-by-default authentication. Every endpoint is protected unless explicitly opted out. The following endpoints require no Authorization header:

Auth

EndpointWhy it's public
POST /auth/challengesOPRF email blind index evaluation
POST /auth/opaque/register-startRegistration step 1
POST /auth/opaque/register-finishRegistration step 2
POST /auth/opaque/authenticate-startLogin step 1
POST /auth/opaque/authenticate-finishLogin step 2
GET /auth/recoveryFetch UMK backup
POST /auth/recoveryComplete account recovery
POST /auth/tokens/refreshToken rotation (body-token auth, no session)

Platform

EndpointWhy it's public
GET /public-keys/serverClients need the platform public key to wrap DEKs before they have a session
POST /verificationsSeal and capability token verification must work without a session

Grants

EndpointWhy it's public
GET /grantsAnonymous grant discovery via view tags — recipients don't need a session to check for incoming grants
DELETE /grants/{id}/claimGrantee self-revoke via claim token — authenticates by claim token, not session

Users

EndpointWhy it's public
GET /users/{userId}/public-keysGrant senders need to look up recipient public keys without the recipient being online

All other endpoints require a valid session token.

On this page