Kinde
Kinde AuthStrategy — verifies Kinde-issued JWTs via JWKS at the configured issuer (no client secret) and maps org_code → tenantId.
@voltro/plugin-auth-kinde integrates Kinde: an AuthStrategy (conforming to @voltro/protocol) that verifies Kinde-issued JWTs against the issuer's JWKS endpoint (public-key verify — no client secret), maps Kinde's org_code to the framework's tenantId, and composes with other strategies (your password cookie, an API key, another IdP). It's a thin specialization of the shared jwtBearerStrategy — it adds Kinde's defaults and performs no verification of its own. Verified claims arrive on the Subject as metadata.provider === 'kinde' + metadata.claims.
Install
pnpm add @voltro/plugin-auth-kindeWiring
Add kindeStrategy(...) to auth.strategies in your api's app.config.ts. The built-in signed-cookie password strategy always runs first; your strategies run after it, in order, until one matcheds (or one faileds).
// app.config.ts
import { kindeStrategy } from '@voltro/plugin-auth-kinde'
export default {
type: 'api' as const,
name: 'myApi',
store: 'postgres' as const,
auth: {
strategies: [
kindeStrategy({
issuer: 'https://yourcompany.kinde.com', // required
// audience: '…', // your Kinde API audience
// defaultTenantId: 'public', // fallback for single-tenant setups
// scopesFromClaims: (c) => c.permissions ?? [], // Kinde permissions → scopes
}),
],
},
}issuer is the only required option (trailing slash is normalised). From it the JWKS URL defaults to <issuer>/.well-known/jwks. The cookie defaults to kinde_access_token (for apps that mirror the SDK's token into a cookie for SSR; pass null for header-only).
Tenant resolution maps from claims.org_code, falling back to claims.org_codes[0], then defaultTenantId. Supply tenantIdFromClaims(claims) to derive it (returning null is a failed verdict). scopesFromClaims maps verified claims onto Subject.scopes — Kinde emits a permissions array and a roles list (e.g. roles.map((r) => r.key)).
To accept your own password sessions AND Kinde during a migration, build the chain explicitly with composeAuthStrategies (see auth strategies):
import { composeAuthStrategies } from '@voltro/protocol'
import { voltroPasswordStrategy } from '@voltro/plugin-auth'
import { kindeStrategy } from '@voltro/plugin-auth-kinde'
const resolve = composeAuthStrategies([
voltroPasswordStrategy(),
kindeStrategy({ issuer: process.env.KINDE_ISSUER! }),
])Options
| Option | Default | Notes |
|---|---|---|
issuer |
— (required) | e.g. https://acme.kinde.com. Builds JWKS URL. |
audience |
(off) | Your Kinde API audience. |
jwksUrl |
<issuer>/.well-known/jwks |
Override. |
algorithms |
['RS256'] |
Allowlist; override for a non-default Kinde signing alg. |
cookieName |
'kinde_access_token' |
null for header-only. |
tenantIdFromClaims |
(uses org_code → org_codes[0]) |
Derive tenantId; null → failed. |
defaultTenantId |
(none) | Fallback for single-tenant setups. |
scopesFromClaims |
(none) | Map claims → Subject.scopes. |
Environment variables
None read directly — pass issuer / audience explicitly (typically from your own defineEnv-declared vars).
Security
Server-only; fails closed — a forged, expired, or wrong-audience token, or one with no resolvable tenant, yields failed, never matched. JWKS public-key verify only; no Kinde client secret is ever handled.