Auth0

Auth0 AuthStrategy — verifies Auth0-issued JWTs via the tenant's JWKS (no client secret) and maps a namespaced custom claim → tenantId.

@voltro/plugin-auth-auth0 integrates Auth0: an AuthStrategy (conforming to @voltro/protocol) that verifies Auth0-issued JWTs against the tenant's JWKS endpoint (public-key verify — no client secret), maps a namespaced custom claim 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 Auth0's defaults and performs no verification of its own. Verified claims arrive on the Subject as metadata.provider === 'auth0' + metadata.claims.

Install

pnpm add @voltro/plugin-auth-auth0

Wiring

Add auth0Strategy(...) 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 { auth0Strategy } from '@voltro/plugin-auth-auth0'

export default {
  type: 'api' as const,
  name: 'myApi',
  store: 'postgres' as const,
  auth: {
    strategies: [
      auth0Strategy({
        domain:   'acme.us.auth0.com',                 // required — builds JWKS URL + issuer
        audience: 'https://api.acme.com',              // your API identifier
        // tenantClaim: 'https://voltro.dev/tenant',    // default namespaced URN
        // defaultTenantId: 'public',                   // fallback for single-tenant setups
        // scopesFromClaims: (c) => c.permissions ?? [], // Auth0 RBAC permissions → scopes
      }),
    ],
  },
}

domain is the only required option (protocol + trailing slash are stripped). From it: JWKS URL defaults to https://<domain>/.well-known/jwks.json and issuer to https://<domain>/the trailing slash matters for Auth0 (override issuer for a custom domain).

Tenant resolution maps from tenantClaim (default the namespaced URN https://voltro.dev/tenant — Auth0's rules require non-standard claims to live under a URN). Use a custom Auth0 Action to copy your app metadata into the namespaced claim, or point tenantClaim at an unnamespaced field that already exists in your token. Supply tenantIdFromClaims(claims) to derive it (returning null is a failed verdict). scopesFromClaims maps verified claims (the permissions array with RBAC enabled, or the space-delimited scope claim) onto Subject.scopes.

To accept your own password sessions AND Auth0 during a migration, build the chain explicitly with composeAuthStrategies (see auth strategies):

import { composeAuthStrategies } from '@voltro/protocol'
import { voltroPasswordStrategy } from '@voltro/plugin-auth'
import { auth0Strategy } from '@voltro/plugin-auth-auth0'

const resolve = composeAuthStrategies([
  voltroPasswordStrategy(),
  auth0Strategy({ domain: process.env.AUTH0_DOMAIN!, audience: process.env.AUTH0_AUDIENCE! }),
])

Options

Option Default Notes
domain — (required) e.g. acme.us.auth0.com. Builds JWKS URL + issuer.
audience (off) Your API identifier. String or array.
jwksUrl https://<domain>/.well-known/jwks.json Override.
issuer https://<domain>/ Trailing slash matters; override for custom domains.
cookieName null (header-only) Auth0's hosted flow uses Bearer tokens.
tenantClaim 'https://voltro.dev/tenant' Namespaced URN.
tenantIdFromClaims (uses tenantClaim) Derive tenantId; nullfailed.
defaultTenantId (none) Fallback for single-tenant setups.
scopesFromClaims (none) Map claims → Subject.scopes.

Algorithms are pinned to ['RS256'] (no alg-confusion).

Environment variables

None read directly — pass domain / 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 Auth0 client secret is ever handled.

See also