Reference
The full defineConfig option tables for server and client.
Server — defineConfig (@adonis-agora/authkit-server)
AuthServerConfigInput:
| Option | Type | Default | Notes |
|---|---|---|---|
issuer | string | — | Public provider URL; must end with mountPath. |
adapter | AdapterFactory | — | Storage adapter, e.g. adapters.database({ connection }). |
clients | ClientConfig[] | — | Internal/test use only. Create clients via the admin console, Admin API (node ace authkit:clients:create), or Dynamic Registration. |
jwks | JwksConfig | — | { source: 'managed' | 'jwks', algorithm?, rotationDays?, keys? }. |
ttl | TtlConfig | see below | Token/session lifetimes; accepts '15m', '30d', or seconds. Declaring this key locks the token_ttl setting. |
globalRolesClaim | string | 'roles' | Claim name where global roles are emitted. |
resolveTokenRoles | (account, ctx) => string[] | Promise<string[]> | account.globalRoles ?? [] | The pluggable seam for role emission — see Customizing auth. Called at mint time for first-party clients (see firstPartyClients), on token-exchange, and on the admin-console gate; ctx carries { clientId?, activeOrg? }. This is what lets an external authority (typically @adonis-agora/authz) own roles instead of a column on the account model. |
cookieKeys | string[] | [] | Cookie signing keys. |
observability | ObservabilityConfig | {} | { metrics?, jsonRoutes?, dashboard? }. |
accountStore | AccountStore | — | Primary identity contract; derives findAccount/verifyCredentials. AccountStore = CoreAccountStore & Partial<Mfa/Webauthn/ProviderIdentity capabilities>; narrow with supportsMfa/supportsPasskeys/supportsProviderIdentity. See Account Store. |
patStore | PatStore | — | Optional; required only for PAT flows. |
mountPath | string | '/oidc' | Where the host kit mounts the OIDC routes. |
routes | boolean | AuthHostOptions | absent (no auto-mount) | Mount the host kit from the config instead of calling registerAuthHost in start/routes.ts. true mounts with config defaults, an object supplies structural defaults (accountRoutes, account, accountLoginUrl, mountPath). Calling registerAuthHost afterwards throws — see Host Kit. |
accountHome | string | '/account/security' | Default destination for the account area: post-login at /account/login (without return_to), e-mail confirmations, and non-admin redirects away from the console. Point it at your app's home to land users straight in the product. |
render | AuthHostRenderer | edgeRenderer | inertiaRenderer to render the screens as Inertia pages, or edgeRenderer for the bundled Edge templates. |
interactionRecovery | { mode?: 'screen' | 'redirect'; redirectTo?: string } | { mode: 'screen' } | What happens when the OIDC interaction session is lost or expired: render the themeable session-expired screen, or 302 to the login. See Host Kit. |
branding | BrandingConfig | — | Per-client theming. Its firstParty list is also read as a fallback allowlist for the authorization claims, but prefer firstPartyClients for that — an authorization decision should not live in a theming block. |
firstPartyClients | string[] | — (no allowlist) | Which clients receive the authorization claims (<globalRolesClaim>, org_id, org_slug, org_role). Omit it and every registered client is first-party — the claims are still gated by client registration and by the client asking for scope=roles. Declare it to restrict them to your own apps; an explicitly empty list means nobody. See Customizing auth. |
social | AuthSocialConfig | — | { providers: string[] }; opt-in social login. |
patIntrospectionSecret | string | — | Shared secret authenticating PAT introspection. |
rateLimit | RateLimitConfigInput | disabled | Opt-in anti-brute-force throttles. Declaring this key locks the rate_limit runtime setting — see Config locks. Leave it out to let the console own the buckets. |
lockout | LockoutConfigInput | enabled | Progressive per-email lockout. store is infra. Declaring this key locks the lockout runtime setting. See Account Lockout. |
mail | MailHooks | — | Pluggable email hooks: onPasswordReset, onEmailVerification, onMagicLink, onSudoLink, onNewDeviceLogin, onOrgInvitation, onEmailChangeConfirm, onEmailChangeNotice, onOtpUnlock, onSecurityNotice, onAccountExpirationWarning, plus from and origin. Every hook is optional — without one the host kit sends its own branded, translated email. See Security — Mail hooks. |
sudo | { methods?: SudoMethod[] } | derived from the rest of the config | The identity-confirmation methods offered on /account/confirm. Omitting it derives a satisfiable list; declaring it turns derivation off and the list is taken literally. Declaring sudo.methods locks the sudoMethods route option. See Console session & sudo. |
audit | AuditSink | no-op | Best-effort audit sink. |
events | EventsConfigInput | — | { onEvent?, webhook? } — observe every audit event in-process or via an HMAC-signed webhook. See Events & Webhooks. |
mfaIssuer | string | 'AuthKit' | TOTP issuer label shown in authenticator apps. |
webauthn | WebauthnConfigInput | derived from issuer | Passkey RP params. See WebAuthn. |
i18n | I18nConfig | English (en); pt-BR built in | Host-kit screen translations. See Internationalization. |
dynamicRegistration | DynamicRegistrationConfigInput | disabled | RFC 7591/7592. See Dynamic Registration. |
admin | AdminConfigInput | disabled | The /admin console: { enabled, roles?, impersonation? }. impersonation: false unregisters the RFC 8693 grant outright and 404s the console panel — see Impersonation. Declaring this key locks the admin_impersonation setting and the admin route option (the prefix stays overridable). See Admin Console. |
adminApi | true | { prefix?: string } | disabled | The Admin REST API (API-key auth). true mounts at the default /api/authkit/v1; { prefix: '/custom/path' } overrides it (normalised: leading /, no trailing slash). Must match apiPrefix in the SDK's remote driver. Declaring this key locks the adminApi route option. See Admin REST API. |
deviceFlow | { enabled: boolean } | disabled | Device Authorization Grant (RFC 8628). See Device Flow. |
dpop | { enabled: boolean } | disabled | DPoP sender-constrained tokens (RFC 9449). See Security. |
par | { enabled: boolean; requirePushedAuthorizationRequests?: boolean } | disabled | Pushed Authorization Requests (RFC 9126). See Security. |
stepUp | { acrValues?: string[]; mfaAcr?: string } | mfaAcr: 'urn:authkit:mfa' | Step-up auth via acr_values. See Security. |
trustedDevices | TrustedDevicesConfigInput | enabled, 30 days | Skip MFA on a trusted device via an encrypted cookie. Declaring this key locks the trusted_devices setting. Step-up authentication always ignores the cookie. See MFA. |
uploads | { avatars?: { disk?: string; directory?: string; maxSizeMb?: number } } | app's default drive disk, authkit/avatars, 5MB | Avatar upload via the app's @adonisjs/drive; degrades to URL input when drive is absent. See Host Kit. |
passwordless | PasswordlessConfigInput | all disabled | Magic-link email login, passkey-first login, and password-free public sign-up. See Passwordless. |
authMethods | AuthMethodsConfigInput | {} | Pin login methods in the file. Each field you declare wins over the auth_methods runtime setting and the console shows it read-only. Declaring this key locks the auth_methods setting. See Config locks. |
login | LoginConfigInput | all off | Login policy: requireVerifiedEmail (capability-probed) and otp (six-digit email codes — see Passwordless). Declaring login.requireVerifiedEmail locks the require_verified_email setting. |
registration | { enabled?: boolean } | { enabled: true } | Allow public sign-up. Declaring this key locks the registration setting — the console can no longer open or close sign-up. Leave it out to keep that switch in the console. |
accessTokens | AccessTokensConfig | { format: 'opaque' } | JWT access tokens (RFC 9068) and per-resource config. See Security. |
botProtection | BotProtectionConfigInput | disabled | Pluggable CAPTCHA/challenge for login, signup, reset; the host supplies verify. Fail-safe: a verify error or timeout lets the request through. Declaring this key locks the bot_protection setting. See Security. |
notifications | — | — | Removed. New-device and new-IP email alerts are managed via the notifications runtime setting (default: both on). See Runtime Settings and Security. |
resolveGeo | (ip: string) => Promise<string | null> | — | Pluggable IP → location lookup for session context. See Admin Console. |
organizations | OrganizationsConfigInput | auto (on when the three org tables exist) | Multi-tenancy. Declaring this key locks the organizations_policy setting. See Organizations. |
schema | { autoManage?: boolean; connection?: string } | { autoManage: true } | Auto-creation of the eight tables AuthKit owns. autoManage: false hands you the job — call ensureAuthkitSchema(db) from a migration. See Runtime Settings — Schema. |
accountLifecycle | { durable?: boolean } | { durable: false } | durable: true enqueues account deletion and export as durable workflows instead of running the cascade in-process. See Compliance — Durable workflows. |
adonisAuth | { guard: string } | absent | Opt-in @adonisjs/auth integration: the account console's login/logout also drive ctx.auth.use(guard), so middleware.auth(), ctx.auth.user and the Bouncer work natively. Pair with authkitUserProvider in config/auth.ts. See AdonisJS Auth integration. |
Ten of the options above are policy, and declaring one in defineConfig locks the
matching runtime setting: registration, authMethods, login.requireVerifiedEmail,
lockout, rateLimit, trustedDevices, botProtection, organizations,
admin.impersonation and ttl. A locked key reads back as null, writes from the console
and the Admin API fail with 423 Locked, and the console renders the control disabled.
Five more — social, rateLimit, sudo.methods, admin and adminApi — lock the
matching registerAuthHost argument the same way. Read
Config locks before you decide where a policy belongs.
Watch the keys whose config type carries only infrastructure: declaring
lockout: { store: 'redis' }, rateLimit, trustedDevices or organizations for
plumbing reasons still locks the whole setting key, freezing its policy at the library
defaults rather than at anything you wrote.
ttl defaults
| Field | Default |
|---|---|
accessToken | 900 (15m) |
refreshToken | 2592000 (30d) |
idToken | 900 (15m) |
session | 604800 (7d) |
ClientConfig
| Field | Type | Notes |
|---|---|---|
clientId | string | — |
clientSecret | string? | Omit for public clients. |
redirectUris | string[] | Allowed authorization redirect URIs. |
postLogoutRedirectUris | string[]? | For RP-initiated logout. |
grants | string[]? | e.g. authorization_code, refresh_token, urn:ietf:params:oauth:grant-type:token-exchange. |
tokenEndpointAuthMethod | 'client_secret_basic' | 'client_secret_post' | 'none' | 'none' for public/SPA + PKCE. |
RateLimitConfigInput
Infrastructure fields only. Bucket policy (login, introspection) is managed via the rate_limit runtime setting.
| Field | Type | Default |
|---|---|---|
enabled | boolean | false |
store | string? | host default store |
Client — defineConfig (@adonis-agora/authkit-client)
ClientConfigInput:
| Option | Type | Default | Notes |
|---|---|---|---|
issuer | string | — | IdP issuer URL. |
clientId | string | — | This client's id. |
clientSecret | string? | — | For confidential clients. |
redirectUri | string | — | This client's callback URI. |
resolver | ResolverFactory | — | resolvers.jwt(...) or resolvers.pat(...). |
resolveUser | (identity, ctx) => Promise<unknown> | — | Map the IdP identity onto your own user. lucidMirror(...) is the ready-made implementation for a Lucid model — see Client. |
sessionKey | string | 'authkit' | Session key for the token set. |
scopes | string[] | ['openid','profile','email','offline_access','roles'] | Requested scopes. Keep offline_access — without it the IdP issues no refresh token, and impersonation breaks the moment the admin's access token expires. |
globalRolesClaim | string | 'roles' | Claim to read global roles from. |
backchannelLogout | BackchannelLogoutInput | — | The batteries-included back-channel logout path: { store } derives the callback, persists revocations, and pairs with BackchannelRevocationMiddleware. See Back-Channel Logout. |
onBackchannelLogout | BackchannelLogoutCallback | — | Invoked with { sid, sub } on a valid logout token. Runs after the backchannelLogout store when both are set. |
sessionIndex | SessionIndex | — | Maps OP sid/sub to local session ids — the manual alternative to backchannelLogout: { store }. |
resilience | ResiliencePolicy | — | Optional policy wrapped around the outbound HTTP calls to the IdP (discovery + token endpoints). Pass the result of wrap(...) from @adonis-agora/resilience; typed structurally, so the client never imports that package. |
Resolver factories
| Factory | Config | Notes |
|---|---|---|
resolvers.jwt | { tokenSource?, jwksUri? } | Local JWKS validation; tokenSource defaults to 'session', jwksUri to ${issuer}/jwks. |
resolvers.pat | { introspectionUrl, introspectionSecret } | Validates PATs via the IdP introspection endpoint. |
resolvers.opaque | { tokenSource?, introspectionUrl?, cacheTtlMs? } | Per-request introspection of opaque access tokens (immediate revocation). Requires a confidential client. |
Route registration
| Export | Purpose |
|---|---|
registerOidcClient(router, options) | Registers /auth/login, the callback, /auth/logout and the back-channel endpoint in one call, with PKCE, state, and role-based post-login redirects. The path most relying parties want — see Client. |
authkitClientGuard / AuthkitClientGuard | @adonisjs/auth guard backed by the AuthKit session, so ctx.auth.user and middleware.auth() work natively. See AdonisJS Auth integration. |
getAuthkit(ctx) | Read the request's Authenticator without going through the container. |
AuthkitContextMiddleware | Populates the per-request identity. |
lucidMirror(options) | A resolveUser that mirrors the IdP identity into a Lucid model. |
createUserinfoResolver(options) | Builds a resolveUser that fetches the OP's userinfo endpoint. |
conventionEndpoints(issuer) | The endpoint map by convention, skipping discovery. |
lucidRevocationStore / BackchannelRevocationMiddleware / DEFAULT_REVOCATION_TABLE | The batteries-included back-channel logout path. |
Login primitives
Use these only when registerOidcClient cannot express your flow.
| Export | Purpose |
|---|---|
generatePkce() | Build a PKCE { verifier, challenge, method: 'S256' }. |
buildAuthorizeUrl(params) | ${issuer}/auth authorization URL. |
exchangeCode(params) | Swap an authorization code for a TokenSet. |
refreshTokens(params) | grant_type=refresh_token refresh (handles rotation). See Refresh Tokens. |
exchangeToken(params) | RFC 8693 token-exchange (impersonation). |
buildEndSessionUrl(params) | ${issuer}/session/end RP-initiated logout URL. |
SDK — createAuthkit (@adonis-agora/authkit-sdk)
createAuthkit(options): Promise<Authkit> builds the backend SDK.
| Export | Purpose |
|---|---|
createAuthkit({ mode: 'remote', baseUrl, apiKey, apiPrefix?, fetchImpl? }) | HTTP driver over the Admin REST API. apiPrefix defaults to '/api/authkit/v1'; override when the server uses a custom adminApi.prefix. |
createAuthkit({ mode: 'embedded', app }) | In-process driver resolving server services from the AdonisJS container. |
createRemoteAuthkit(options) | The remote driver directly, when you already know you want HTTP and would rather skip the mode discriminator. |
createEmbeddedAuthkit(options) | The embedded driver directly. |
AuthkitApiError | Error thrown by the remote driver (status, code, message). |
See the SDK guide for the full method table.
Runtime Settings
An optional, capability-probed mechanism for persisting configuration at runtime in the
auth_settings table. The table is detected on first access; all operations degrade
gracefully when it is absent (fail-safe).
auth_settings schema
CREATE TABLE auth_settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL, -- JSON
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by TEXT -- nullable account id of the admin
);Create the table with a migration of your own. AuthKit never creates it automatically — it is an opt-in capability.
RuntimeSettings
Class from @adonis-agora/authkit-server (internal, used by the admin console and
API controllers). Accepts a Lucid Database instance and an optional ttlMs (default
15_000 — 15 s). Implements SettingsCapability.
| Method | Notes |
|---|---|
getSetting(key) | Returns unknown | null. Null = table absent or key not found. Cached with TTL. |
setSetting(key, value, updatedBy?) | Upsert. Invalidates cache. No-op when the table is absent. |
deleteSetting(key) | Delete. Invalidates cache. No-op when the table is absent. |
listSettings() | Returns SettingRow[]. No cache (low-frequency). Empty array when the table is absent. |
isTablePresent() | Returns boolean. Result is memoised (probed once per instance). |
invalidate(key?) | Manually clear the in-memory cache (all keys when called without argument). |
SettingsCapability and supportsSettings
import { supportsSettings } from '@adonis-agora/authkit-server'
if (supportsSettings(store)) {
const raw = await store.getSetting('bot_protection')
}supportsSettings(obj) is a type guard that returns true when obj exposes a
getSetting function (i.e. implements SettingsCapability).
Known setting keys
Every key below is capability-probed: if the auth_settings table is absent, it falls back
to the lib default (fail-safe). Precedence has two cases. When defineConfig does not
declare the matching field, the persisted setting wins over the lib default. When it
does, the key is locked — getSetting reads back null so the config value stands,
and writes fail with 423 Locked. See Config locks for the ten
lockable keys, and Runtime Settings for the canonical guide, the CLI, and
migration from static config.
| Key | Shape | Lib default | Notes |
|---|---|---|---|
bot_protection | { enabled: boolean; on?: ('login'|'signup'|'reset')[] } | { enabled: false } | Runtime CAPTCHA toggle. See Security — Runtime toggle. |
registration | { enabled: boolean } | { enabled: true } | Open or close public sign-up. Overrides the static registration.enabled in defineConfig. See Security — Registration toggle. |
require_verified_email | { enabled: boolean; graceDays?: number } | { enabled: false, graceDays: 0 } | Block unverified accounts. graceDays allows newly-registered accounts to log in for N days without verifying. Capability-probed (isEmailVerified). See Compliance — Verified email gate. |
maintenance_mode | { enabled: boolean; message?: string } | { enabled: false } | Block login/sign-up/forgot for non-admins. Admin API escape hatch. See Security — Maintenance mode. |
auth_methods | { password?: boolean; magicLink?: boolean; passkey?: boolean; passkeyAutofill?: boolean; social?: string[]; forgotPassword?: boolean } | config/capability defaults | Filter login methods at runtime. passkeyAutofill: WebAuthn conditional mediation (default true when passkey on). forgotPassword auto-off when password off. social intersected with static providers. Fail-safe all-off reverts to defaults. See Security — Authentication methods. |
email_change | { enabled?: boolean; ttlHours?: number; requirePassword?: boolean } | { enabled: true, ttlHours: 24, requirePassword: true } | Verified email-change flow. See Compliance — Email change. |
security_notifications | { enabled?: boolean; kinds?: string[] } | { enabled: true, kinds: [all 6] } | Security-event emails. kinds: password_changed, mfa_enabled, mfa_disabled, passkey_added, passkey_removed, email_changed. See Compliance — Security notifications. |
password_history | { enabled?: boolean; count?: number } | { enabled: false, count: 5 } | Reuse prevention. Requires auth_password_history table. See Passwords — History. |
password_expiration | { enabled?: boolean; maxAgeDays?: number } | { enabled: false, maxAgeDays: 90 } | Force password rotation. Requires password_changed_at column. See Passwords — Expiration. |
session_policy | { rememberEnabled?: boolean; rememberDays?: number; defaultSessionHours?: number; singleSession?: boolean; idleTimeoutMinutes?: number } | { rememberEnabled: true, rememberDays: 30, defaultSessionHours: 168, singleSession: false, idleTimeoutMinutes: 0 } | Session duration and behaviour. Idle timeout applies to account console only (not OIDC tokens). |
lockout | { enabled?: boolean; maxAttempts?: number; windowSec?: number; baseLockoutSec?: number; maxLockoutSec?: number } | { enabled: true, maxAttempts: 5, windowSec: 900, baseLockoutSec: 60, maxLockoutSec: 3600 } | Per-email lockout policy. store stays in config (infra). |
rate_limit | { login?: { points, duration }; introspection?: { points, duration } } | login: { points: 10, duration: '1 min' }; introspection: { points: 60, duration: '1 min' } | Rate-limit bucket policy. Limitation: route throttle middleware uses boot-time values; only lockout-side is dynamic. |
password_policy | { minLength?: number; requireUppercase?: boolean; requireLowercase?: boolean; requireNumbers?: boolean; requireSymbols?: boolean; checkPwned?: boolean; blockCommon?: boolean } | { minLength: 8, checkPwned: false, blockCommon: true, rest false } | Password complexity, HIBP check, and offline common-password block (~10k list). See Passwords. |
notifications | { newLoginEmail?: boolean; newDeviceEmail?: boolean } | { newLoginEmail: true, newDeviceEmail: true } | New-IP and new-device email alerts. |
trusted_devices | { enabled?: boolean; days?: number } | { enabled: true, days: 30 } | MFA skip via encrypted cookie. secrets stays in config (infra). |
token_ttl | { accessTokenSec?: number; idTokenSec?: number; refreshTokenSec?: number } | { accessTokenSec: 900, idTokenSec: 900, refreshTokenSec: 2592000 } | Token lifetimes (seconds). Takes effect immediately via holder. Session TTL is in session_policy. |
admin_impersonation | { enabled?: boolean } | { enabled: false } | Show impersonation panel in admin console. |
organizations_policy | { allowSelfCreate?: boolean; invitationTtlHours?: number; roles?: string[] } | { allowSelfCreate: false, invitationTtlHours: 168, roles: ['owner','admin','member'] } | Org policies. owner is always preserved. |
sudo_mode | { enabled?: boolean; graceMinutes?: number } | { enabled: true, graceMinutes: 15 } | Re-authentication gate for sensitive account actions. graceMinutes: 0 prompts on every action. The grace is bound to the account that earned it, so switching accounts always re-prompts. See Console session & sudo. |
otp_lockout | { enabled?: boolean; maxAttempts?: number; unlockTtlHours?: number } | { enabled: true, maxAttempts: 5, unlockTtlHours: 24 } | Lock TOTP/recovery after N failures; the account unlocks itself through a single-use emailed link (onOtpUnlock). Requires @adonisjs/limiter. See Security — OTP lockout. |
account_expiration | { enabled?: boolean; inactiveDays?: number; warnDays?: number } | { enabled: false, inactiveDays: 365, warnDays: 14 } | Block inactive accounts. Last activity = last login.success in audit. Warn via onAccountExpirationWarning({ email, expiresInDays }). Scan via authkit:accounts:expire-scan. Requires a queryable audit sink. See Compliance — Account expiration. |
roles_catalog | { roles: { name: string; description?: string }[] } | { roles: [{ name: 'ADMIN', … }] } | The catalogue of assignable global roles. It turns the free-text role field on the console's user page into checkboxes. ADMIN is always merged back in — it is the console's own gate, so it can never be catalogued away. Entries with an empty or non-string name are dropped silently, and the result is sorted by name. Resolves org-scoped first, then global, then the default. |
key_rotation | { enabled?: boolean; maxAgeDays?: number; keep?: number } | { enabled: false, maxAgeDays: 90, keep: 2 } | Automatic signing-key rotation for a managed keystore: rotate once the active key is older than maxAgeDays, retaining keep previous keys so tokens signed before the rotation still verify. Surfaced by the console's key page and by GET {adminApi}/keys. See Security — Signing key rotation. |
Ace commands
Commands provided by @adonis-agora/authkit-server:
| Command | Notes |
|---|---|
node ace authkit:doctor | Validate the config and peer dependencies; emits findings with ok/warn/error levels. Includes checkSettings (bot_protection orphan, require_verified_email capability) and checkClients (informational: adapter volatility warning). |
node ace authkit:eject | Copy the bundled host-kit controllers/pages into your project for customization. |
node ace authkit:users:import --file <path> [--dry-run] | Bulk-import users from a JSON array or NDJSON file, preserving hashes as-is for lazy rehash on login. |
node ace authkit:keys:rotate [--keep=N] [--retire] [--dry-run] | Rotate signing keys in a managed keystore file; keeps a grace-period window of N previous keys (default 2). |
node ace authkit:clients:create --redirect-uri=<uri> [--client-id] [--public] [--grant] [--post-logout-uri] [--backchannel-logout-uri] [--json] | Create an OIDC client in the adapter/DB at runtime. For confidential clients a secret is generated and printed once — save it immediately. See below. |
node ace authkit:settings:list [--json] | List all runtime settings currently persisted in the auth_settings table. |
node ace authkit:settings:get <key> [--json] | Get a specific setting by key. Prints null if the key is not set (lib default applies). |
node ace authkit:settings:set <key> <json> [--json] | Upsert a setting. Validates shape for known keys. Audits settings.updated with updatedBy: 'cli'. |
node ace authkit:settings:unset <key> [--json] | Delete a setting (reset to lib default). Audits settings.updated. |
node ace authkit:accounts:expire-scan [--warn] [--dry-run] [--json] | Scan for expired/expiring accounts. --warn sends mail.onAccountExpirationWarning (deduped via audit). --dry-run prints counts without writing. --json outputs { expired, warned, skipped }. See Compliance — Account expiration. |
authkit:doctor — the full check list
The doctor runs thirty-one checks and prints each one at ok, warn or error. Most are
informational: they tell you which optional capability is actually reachable, given the
config, the account store and the tables that exist. A check stays silent when the feature
it covers is switched off — an opt-in feature you never enabled is not a finding.
| Check | What it looks at |
|---|---|
checkConfigResolves | config('authkit') resolves at all. |
checkIssuer | issuer is a valid URL and its pathname matches mountPath. |
checkClients | No static clients in the config — clients are runtime-only, via the console or the Admin API. |
checkAdapterVolatility | The adapter persists. Because clients live only at runtime, a volatile adapter (in-memory, Redis without persistence) silently loses them on restart. |
checkAccountStore | The store is present, and which capabilities it implements. |
checkSession | @adonisjs/session is configured; warns about the cookie store with large token sets. |
checkShield | The CSRF exceptions Shield needs for mountPath. |
checkAlly | @adonisjs/ally is present when social login is configured. |
checkOtpLockout | OTP lockout is enabled but @adonisjs/limiter is missing, which makes it a no-op. |
checkSudoMode | Reports the effective sudo-mode state. |
checkRateLimit | rateLimit is on but @adonisjs/limiter is missing. |
checkAdmin | admin.enabled without roles; reports the active UI mode. |
checkRequireVerifiedEmail | The gate is on but the store cannot answer isEmailVerified. |
checkBotProtection | Which actions the challenge covers, and a reminder of the fail-safe semantics. |
checkWebauthn | The WebAuthn rpId matches the issuer host. |
checkPasswordPolicy | The policy shape is valid; reports whether the breach check is on. |
checkJwks | Rotation state for a managed keystore; warns when managed without a store (no real rotation). |
checkAccessTokens | The access-token format; in JWT mode, reminds you the JWKS must be persisted so relying parties keep validating across restarts. |
checkOrganizations | Organizations are enabled in the config but the store lacks the capability. |
checkSettings | The auth_settings table's presence; flags an orphan bot_protection row that has no verify behind it. |
checkAuthMethodsSetting | The persisted auth_methods value has a usable shape — notably, that it does not switch every method off. |
checkEmailChange | The store supports the verified email-change flow. |
checkSecurityNotifications | Mail is configured and the store supports the security capability. |
checkPasswordPepper | Reports how the password pepper is configured, and nudges a bare string toward the [new, old] array form that allows rotation without downtime. |
checkPasswordHistory | The auth_password_history table exists when reuse prevention is on. |
checkPasswordExpiration | The password_changed_at column exists when expiration is on. |
checkSessionPolicy | Internally inconsistent values — e.g. an idle timeout longer than the session itself, which can never fire. |
checkRolesCatalog | Warns when admin.roles names a role outside the catalogue, which would leave the console gate unreachable. |
checkAccountExpiration | The audit sink can be queried (otherwise the feature is unavailable) and the setting's fields cohere. |
checkPasskeyAutofill | Conditional mediation can actually be offered — it never appears without WebAuthn configured. |
checkFirstPartyClients | Whether a first-party allowlist is declared. Without one, every registered client that asks for scope=roles receives the roles and organization claims. |
authkit:users:import
See Passwords & Migration for the file format and field list.
authkit:keys:rotate
See Security — Signing key rotation for the flag reference and rotation strategy.
authkit:clients:create
Creates an OIDC client in the adapter/DB at runtime via AdminClientsService — the same
path used by the admin console and Dynamic Registration (RFC 7591). No redeploy needed.
# Public SPA client
node ace authkit:clients:create \
--client-id=acme-web \
--redirect-uri=https://web.acme.com/auth/callback \
--public
# Confidential client (secret printed once)
node ace authkit:clients:create \
--client-id=acme-api \
--redirect-uri=https://api.acme.com/cb \
--grant=authorization_code \
--grant=refresh_token
# Machine-readable JSON output
node ace authkit:clients:create \
--redirect-uri=https://app.acme.com/cb \
--jsonFlags:
| Flag | Notes |
|---|---|
--redirect-uri=<uri> | Allowed callback URI (repeatable). Required. |
--client-id=<id> | Desired client_id. Omit to generate a random UUID. |
--public | Create a public client (no secret; token_endpoint_auth_method=none). Default: confidential. |
--grant=<type> | Grant type (repeatable). Default: authorization_code + refresh_token. |
--post-logout-uri=<uri> | Post-logout redirect URI (repeatable). |
--backchannel-logout-uri=<uri> | OIDC Back-Channel Logout endpoint of the RP. |
--json | Print the result as JSON (includes clientId and clientSecret for confidential clients). |
For confidential clients a clientSecret is generated and printed exactly once — store
it immediately. The admin console and Admin API are alternatives when you prefer a browser
or HTTP interface.
Server backend helpers — @adonis-agora/authkit-server
Helpers for use inside AdonisJS controllers, middleware, and route callbacks.
Console session
| Export | Purpose |
|---|---|
getAccountId(ctx) | Returns string | null — the account the console session currently acts as. During an impersonation session this is the target. |
realAccountId(ctx) | Returns string | null — the human behind the session. Outside impersonation it equals getAccountId; during impersonation it stays the admin. Authorize with this one; getAccountId answers "whose data am I looking at", not "who is allowed to do this". See Impersonation. |
hasAccountSession(ctx) | Returns boolean — true when the console session is present. |
consoleLoginUrl(returnTo?) | The login URL with an optional return_to, honouring an accountLoginUrl override. |
ACCOUNT_SESSION_KEY | Raw session key. Prefer getAccountId/hasAccountSession. |
See Console session & sudo mode for recipes.
Sudo mode (step-up authentication)
| Export | Purpose |
|---|---|
requireSudo(ctx, settings) | Returns true when the caller already holds sudo, and otherwise returns a redirect to the confirmation screen with a return_to. Guard with if (gate !== true) return gate. Both arguments are required: settings is a SettingsCapability or null, and null means "no runtime settings here", so SUDO_MODE_DEFAULTS applies. Fail-safe — a settings error lets the request through. |
isSudoActive(ctx, graceMinutes) | Pure boolean check — true when the step-up timestamp is within the grace window and belongs to the account currently signed in. |
markSudo(ctx) | Stamps the step-up timestamp, bound to the current account. Deprecated as a way to grant sudo — it records no sudo.confirmed audit event, no remembered method and no redirect. Use completeSudo(sudoContextFrom(ctx), methodId) instead. |
SUDO_SESSION_KEY | Raw session key for the timestamp. |
SUDO_ACCOUNT_SESSION_KEY | Raw session key for the account the grace was earned by — checked before the grace window, so a sudo grant cannot survive a switch to another account. |
SUDO_MODE_DEFAULTS | { enabled: true, graceMinutes: 15 } |
resolveEffectiveSudoMode(settings) | Reads the sudo_mode setting from a SettingsCapability, with fail-safe fallback. |
Sudo methods (the confirmation SPI)
The methods offered on the confirmation screen are pluggable. Read Console session & sudo mode for the full guide.
| Export | Purpose |
|---|---|
sudoMethods | The built-in method factories: password(), passkey(), magicLink(), oidcStepUp(). |
completeSudo(context) | Grant sudo from your own handler. Required for oidcStepUp(), which registers no routes — your callback validates the grant, so your callback has to grant the sudo. Doing it with markSudo instead loses the sudo.confirmed audit event, the remembered method, and the return_to redirect. |
failSudo(context, reason) | Record a failed confirmation attempt. |
sudoContextFrom(ctx) | Build the SudoContext the two functions above take. Use it — assembling the context by hand means validating return_to by hand, which is an open redirect waiting to happen. |
LAST_METHOD_SESSION_KEY | Raw session key for the last method used successfully, which is promoted to the top of the screen. |
SudoMethod / SudoContext / SudoMethodDescriptor / SudoRouteHelpers | The SPI types for writing your own method. |
Config locks
| Export | Purpose |
|---|---|
SettingLockedError | Thrown by setSetting/deleteSetting for a key locked by defineConfig. Carries code: 'E_SETTING_LOCKED' and the key; the console and Admin API surface it as 423 Locked. |
isSettingLocked(key) | Is this key locked? |
lockedSettingKeys() | Snapshot of the locked keys — what a custom settings UI reads to render controls as disabled. |
deriveLockedSettingKeys(config) | Derives the locked keys from a raw config input. |
setLockedSettingKeys(keys) | Installs the lock set. The provider calls this once at boot; a host normally never does. |
resetLockedSettingKeys() | Clears the locks — for tests. |
POLICY_ROUTE_OPTIONS / PolicyRouteOption | The five registerAuthHost options that are policy rather than structure. |
See Config locks.
@adonisjs/auth integration
| Export | Purpose |
|---|---|
authkitUserProvider(...) | User provider for config/auth.ts's sessionGuard(), backed by AuthKit's own account store. Pair with adonisAuth: { guard }. |
oidcRpGuard(...) / OidcRpGuard | Guard for a relying party: the identity comes from the session the OIDC callback wrote, so ctx.auth.user works without the RP authenticating anyone itself. |
OidcRpGuardOptions / OidcRpGuardEvents | Its option and event types. |
See AdonisJS Auth integration.
Trusted devices
| Export | Purpose |
|---|---|
resolveTrustedDevices(input) | Resolve the config into its effective form. |
isTrustedDeviceValid(payload, ...) | Is a decoded cookie payload still valid for this account? |
buildTrustedDevicePayload(...) | Build the payload to store in the cookie after a successful MFA challenge. |
TRUSTED_DEVICE_COOKIE | The cookie name. |
Step-up authentication always ignores the cookie — the whole point of a step-up is that a past decision does not answer for it. See MFA — Trusted devices.
Durable account lifecycle
Published from @adonis-agora/authkit-server/durable, and only relevant with
accountLifecycle: { durable: true }. See
Compliance — Durable workflows.
| Export | Purpose |
|---|---|
defineAccountDeletionWorkflow(...) / defineAccountExportWorkflow(...) | Register the two workflows with your app's workflow engine. |
enqueueAccountDeletion(...) / enqueueAccountExport(...) | Enqueue a run. |
enqueueDeletionVia(...) | Enqueue a deletion through a specific engine instance. |
resolveWorkflowEngine(...) | Locate the app's workflow engine. |
Impersonation (relying-party glue)
| Export | Purpose |
|---|---|
rememberAccessToken(ctx, token) | Store the admin's access token so it can be exchanged later. |
rememberRefreshToken(ctx, token) | Store the refresh token. Required — without it, impersonation dies when the admin's access token expires. Needs offline_access in the RP's requested scopes. |
refreshAccessToken(ctx, ...) | Renew the stored access token from the refresh token. |
startImpersonation(params) | Exchange the admin's token for a token representing the target (RFC 8693). |
impersonationState(ctx) | The current ImpersonationState, or none. |
stopImpersonation(ctx) | Drop back to the admin's own session. |
See Impersonation.
Client helpers — @adonis-agora/authkit-client
Additional exports beyond the resolver factories:
| Export | Purpose |
|---|---|
verifyJwtAccessToken(token, options) | Verify a JWT AT (RFC 9068) locally against the IdP's JWKS. See Client. |
clearJwksCache() | Clear the in-process JWKS cache (useful in tests). |
Account Self-Service API — /account/api/*
Session-authenticated JSON API served by @adonis-agora/authkit-server. Mounted
automatically when the account console is enabled (always on by default with registerAuthHost).
Mutating routes require an X-CSRF-TOKEN header. Sensitive actions require an active
sudo session — the server returns 403 { error: { code: 'sudo_required' } } when the
grace period has expired.
| Method | Path | Notes |
|---|---|---|
GET | /account/api/me | Profile + capability flags (capabilities.*) + sudo state |
GET | /account/api/security | Active sessions, MFA status, passkeys, pending email change |
PATCH | /account/api/profile | { name?, avatarUrl? } — update profile |
POST | /account/api/password | { currentPassword, newPassword } — sudo + current password required |
POST | /account/api/email-change | { newEmail, currentPassword? } — sudo required; obeys email_change setting |
POST | /account/api/email-change/cancel | Cancel pending email change |
GET | /account/api/sessions | { supported, sessions[] } |
DELETE | /account/api/sessions/:id | Revoke one session |
POST | /account/api/sessions/revoke-others | Revoke all other OIDC sessions |
POST | /account/api/sessions/revoke-all | Sign out of all devices (including current session) |
GET | /account/api/apps | { supported, apps[] } — OAuth grants |
DELETE | /account/api/apps/:clientId | Revoke an app's grants |
GET | /account/api/mfa | { enabled, totp, passkeys, recovery } |
GET | /account/api/passkeys | { supported, passkeys[] } |
DELETE | /account/api/passkeys/:id | Remove passkey — sudo required |
GET | /account/api/tokens | { supported, tokens[] } — Personal Access Tokens |
POST | /account/api/tokens | { name? } — create PAT (sudo required); secret returned once |
DELETE | /account/api/tokens/:id | Revoke PAT — sudo required |
GET | /account/api/orgs | { supported, activeOrgId, orgs[] } |
GET | /account/api/orgs/invitations | { supported, invitations[] } — pending invitations |
GET | /account/api/orgs/:id | Org detail — requires membership |
All routes are capability-probed: if the store doesn't support a feature, the response
contains { supported: false } instead of an error. The TypeScript types for every response
are exported from @adonis-agora/authkit-react (see below).
React SDK — @adonis-agora/authkit-react
Full export list. For usage and examples see React (Frontend), React Components, and Typed Client & TanStack Query.
Auth state
| Export | Notes |
|---|---|
useAuth() | { user, isAuthenticated, globalRoles, hasGlobalRole, hasAnyGlobalRole, hasAllGlobalRoles }, read from the authkit Inertia shared prop (or an AuthProvider above it). Never throws — a missing prop yields the unauthenticated state |
AuthProvider / AuthContext | Inject auth state outside Inertia |
AuthkitProvider | Configure URLs, endpoints, and the IdP mode |
AuthkitConfigContext / useAuthkitConfig / resolveConfig / buildAuthUrl / DEFAULT_CONFIG | The configuration layer under AuthkitProvider |
Authenticated / Guest | Gating components |
CanPermission | Permission-gating component, backed by the can endpoint |
useCan / checkCan / invalidateCanCache | The hook, the bare check, and the cache reset behind CanPermission |
hasGlobalRole / hasAnyGlobalRole / hasAllGlobalRoles | Pure global-role helpers |
AuthKit authenticates and carries global roles. Per-application authorization —
app-local roles and permissions — belongs to @adonis-agora/authz, and reaches React
through useCan / CanPermission. There is no hasAppRole on ctx.auth and no
app-role helper in this package.
Headless hooks
| Export | Notes |
|---|---|
useSignIn / useSignOut | OIDC redirect |
useUser | Alias over useAuth |
useProfile | { data, loading, error, actions: { update } } |
useSessions | { data, loading, error, actions: { revoke, refetch } } |
useAuthorizedApps | { data, loading, error, actions: { revoke } } |
useOrganizations | { data, loading, error, activeOrgId, supported, actions } |
useOrganization(orgId) | Org detail + members |
useSwitchOrganization | { activate(orgId), deactivate() } |
useOrgInvitations | { data, loading, error, actions: { accept } } |
usePasswordStrength(password, opts?) | { score: 0–4, feedback }; heuristicScorer is the default scorer and can be swapped |
usePasskeyAutofill(opts) | WebAuthn conditional mediation |
usePasskeyLogin(opts) | Passkey sign-in for a custom login screen |
usePasskeyAssertion(opts) / usePasskeyRegistration(opts) | The assertion and registration ceremonies as hooks |
useResource / jsonRequest | The generic JSON primitive every headless hook is built on |
Passkeys — the three tiers
| Tier | Export |
|---|---|
| Component | PasskeyButton |
| Hooks | usePasskeyLogin, usePasskeyAssertion, usePasskeyRegistration, usePasskeyAutofill |
| Functions | authenticatePasskey, registerPasskey, submitPasskeyVerification, runPasskeyAssertion, runPasskeyRegistration, submitClassicForm, loadStartAuthentication, loadStartRegistration |
Reach for the component on a stock login screen, the hook inside your own React screen, and the function when your screen owns its state machine. See React and Components.
Interaction URLs
| Export | Notes |
|---|---|
interactionUrls | Typed builders for the interaction endpoints |
oauthRedirectUrl | The OAuth redirect URL builder |
OTP_CODE_FIELD | The form field name the OTP step posts |
InteractionUrls / InteractionPostStep | Their types |
Typed client
| Export | Notes |
|---|---|
createAuthkitClient(opts?) | Factory — returns AuthkitClient |
AuthkitClientError | { status, code?, message, body, isUnauthorized } |
AuthkitClientProvider | React context provider for the client |
useAuthkitClient() | Read the client from context |
createAuthkitQueryClient() | Pre-configured QueryClient (staleTime 30s, gcTime 5min, retry 1) |
Query keys
| Export | Notes |
|---|---|
authkitKeys.admin.* | Structured keys for all admin queries, including admin.keys() |
authkitKeys.account.* | Structured keys for all account queries |
authkitKeys.admin.settings(orgId?) | Settings key; without an orgId it is the global scope |
Admin query hooks
useOverviewQueryOptions, useUsersQueryOptions, useUserQueryOptions,
useUserSessionsQueryOptions, useSessionsQueryOptions, useClientsQueryOptions,
useClientQueryOptions, useRolesQueryOptions, useOrgsQueryOptions, useOrgQueryOptions,
useAuditQueryOptions, useSettingsQueryOptions, useImpersonationQueryOptions,
useKeysQueryOptions.
Admin mutation hooks
useCreateUserMutationOptions, useUpdateUserMutationOptions, useDisableUserMutationOptions,
useEnableUserMutationOptions, useResetPasswordMutationOptions, useDeleteUserMutationOptions,
useRevokeUserSessionsMutationOptions, useRevokeAllSessionsMutationOptions,
useCreateClientMutationOptions, useUpdateClientMutationOptions, useDeleteClientMutationOptions,
useRegenerateClientSecretMutationOptions, useCreateRoleMutationOptions,
useUpdateRoleMutationOptions, useDeleteRoleMutationOptions, useCreateOrgMutationOptions,
useUpdateOrgMutationOptions, useDeleteOrgMutationOptions,
useAddOrgMemberMutationOptions, useRemoveOrgMemberMutationOptions, useUpdateOrgMemberRoleMutationOptions,
useCreateOrgInvitationMutationOptions, useRevokeOrgInvitationMutationOptions,
useSetSettingMutationOptions, useRemoveSettingMutationOptions,
useRotateKeysMutationOptions.
Account query hooks
useMeQueryOptions, useSecurityQueryOptions, useAccountSessionsQueryOptions,
useAppsQueryOptions, useMfaQueryOptions, usePasskeysQueryOptions, useTokensQueryOptions,
useAccountOrgsQueryOptions, useAccountOrgQueryOptions, useAccountOrgInvitationsQueryOptions.
Account mutation hooks
useUpdateProfileMutationOptions, useChangePasswordMutationOptions,
useEmailChangeMutationOptions, useCancelEmailChangeMutationOptions,
useRevokeSessionMutationOptions, useRevokeOtherSessionsMutationOptions,
useAccountRevokeAllSessionsMutationOptions,
useRevokeAppMutationOptions, useRemovePasskeyMutationOptions,
useCreateTokenMutationOptions, useRevokeTokenMutationOptions.