Installing an SDK
The five packages we publish, what each one is for, and the configuration each one takes.
Five packages, all Apache-2.0, all optional: Kleora is an OAuth 2.1 and OpenID Connect provider, and a generic client library works against it.
| Package | What it is for | Needs |
|---|---|---|
@kleora-io/browser | Signing in from a single-page application. | Node 22+ to build; evergreen browsers |
@kleora-io/nuxt | The same, as a Nuxt module. | Nuxt 4, Node 22+ |
@kleora-io/node | Verifying access tokens on a Node API, and calling the management API. | Node 20+ |
kleora | The same, in Python. | Python 3.12+ |
django-kleora | The Django integration, over kleora. | Python 3.12+ |
@kleora-io/browser
The authorization-code-with-PKCE client. No runtime dependencies, ES modules only.
npm install @kleora-io/browser
createKleora() takes three required options and four optional ones:
| Option | Default | Meaning |
|---|---|---|
issuer | — | The environment's address, e.g. https://acme.kleora.eu. A trailing slash is normalised away. |
clientId | — | The client's client_id. It must be a spa client. |
redirectUri | — | One of that client's registered redirect URIs, matched exactly. |
scope | openid profile email offline_access | Drop offline_access and no refresh token is issued, so the token cannot be renewed silently. |
audience | — | The environment's API audience, if you have set one. |
tenant | — | Pins every sign-in to one workspace slug. |
storage | memory | memory or session. Never localStorage. |
The quickstart walks through a working page end to end.
@kleora-io/nuxt
A Nuxt 4 module wrapping the browser SDK: auto-imported composables, a route
guard, a callback page and an authenticated $fetch instance.
npm install @kleora-io/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@kleora-io/nuxt'],
kleora: { issuer: '<issuer>', clientId: '<client_id>' },
})
There is no redirectUri in that config because the module defaults it to its
own callback page on the site's own origin — /auth/callback. That is still the
URI you register on the client.
What the module gives you:
| Export | What it is |
|---|---|
useKleora() | { client, user, isAuthenticated, isLoading, hasLikelySession, login, logout, getAccessToken, restore, settle }. |
useKleoraUser() | A Ref holding the signed-in user, or null. |
useKleoraApi(), $kleoraApi | A $fetch instance with your API's base URL and a bearer token on every request. |
middleware kleora-auth | Registered globally, acts only on pages that declare definePageMeta({ auth: true }). |
/auth/callback | A page the module adds, which completes the sign-in and returns you where you started. |
Every option can come from the environment instead of the config file:
NUXT_PUBLIC_KLEORA_ISSUER, …_CLIENT_ID, …_REDIRECT_URI, …_SCOPE,
…_AUDIENCE, …_TENANT, …_STORAGE, and NUXT_PUBLIC_API_BASE for the API
base URL. The one exception is callbackPath, which decides where the callback
page is mounted and is therefore fixed at build time.
@kleora-io/node
Access-token verification with a JWKS cache, Express and Fastify middleware, and a typed client for the management API. Its only runtime dependency is a JOSE library; Express and Fastify are optional peers.
npm install @kleora-io/node
Verification is framework-free — authenticate() takes anything with a
headers.authorization property — and verifyAccessToken() takes the bare
string. Both return the token's claims: sub, tenant, roles,
permissions, scope, env and the rest.
kleora (Python)
The same verification in Python, plus a management client in both a synchronous and an asynchronous flavour.
pip install kleora
import { verifyAccessToken } from '@kleora-io/node'
const claims = await verifyAccessToken(token, {
issuer: 'https://acme.sandbox.kleora.eu',
audience: '<client_id>',
})
Both fetch your environment's key set once, cache it for as long as the response says to, and fail closed if it becomes unreachable. Neither reads an environment variable: the issuer, the audience and any API key are passed in by you.
django-kleora
A django-ninja authentication class, a middleware, a permission decorator and a
KLEORA settings dict, over kleora, which it installs for you. Nothing in it
verifies a token itself: the key-set cache and the token errors are kleora's.
pip install django-kleora
# settings.py
INSTALLED_APPS = [..., "django_kleora"]
MIDDLEWARE = [..., "django_kleora.KleoraAuthMiddleware"]
KLEORA = {
"ISSUER": "https://acme.sandbox.kleora.eu",
"AUDIENCE": "<client_id>",
}
from django_kleora import KleoraAuthentication, requires_permission
from ninja import NinjaAPI
api = NinjaAPI(auth=KleoraAuthentication())
@api.get("/invoices")
@requires_permission("invoices:read")
def invoices(request):
return {"tenant": request.kleora.tenant}
requires_permission answers 401 when there is no verified token, 403 when
the token lacks the permission, and 503 when the issuer's key set could not be
read — the same three answers, and the same problem documents, as the Node
middleware. The middleware is for views django-ninja never sees: it sets
request.kleora and never refuses a request itself.
No SDK at all
The hosted pages and the OAuth endpoints are standard. Everything a generic
OpenID Connect client needs is at
{issuer}/.well-known/openid-configuration, and the key set it names is what
verifies a token. Authorization code with PKCE is the only browser flow we
support; implicit and password grants are not offered.