Skip to content

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.

PackageWhat it is forNeeds
@kleora-io/browserSigning in from a single-page application.Node 22+ to build; evergreen browsers
@kleora-io/nuxtThe same, as a Nuxt module.Nuxt 4, Node 22+
@kleora-io/nodeVerifying access tokens on a Node API, and calling the management API.Node 20+
kleoraThe same, in Python.Python 3.12+
django-kleoraThe 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:

OptionDefaultMeaning
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.
scopeopenid profile email offline_accessDrop 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.
storagememorymemory 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:

ExportWhat it is
useKleora(){ client, user, isAuthenticated, isLoading, hasLikelySession, login, logout, getAccessToken, restore, settle }.
useKleoraUser()A Ref holding the signed-in user, or null.
useKleoraApi(), $kleoraApiA $fetch instance with your API's base URL and a bearer token on every request.
middleware kleora-authRegistered globally, acts only on pages that declare definePageMeta({ auth: true }).
/auth/callbackA 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.