Register config/auth.ts
The configure hook exports an initialized key set and refresh store from config/jwt.ts. Use this
exact guard registration:
import { defineConfig } from '@adonisjs/auth'
import { jwtGuard } from '@rikology/adonisjs-jwt'
import { lucidJwtUserProvider } from '@rikology/adonisjs-jwt/providers'
import jwtConfig, { jwtKeySet, jwtRefreshTokenStore } from '#config/jwt'
const authConfig = defineConfig({
default: 'jwt',
guards: {
jwt: jwtGuard({
provider: lucidJwtUserProvider({ model: () => import('#models/user') }),
keySet: jwtKeySet,
refreshStore: jwtRefreshTokenStore,
config: jwtConfig,
}),
},
})
export default authConfigProtect routes with middleware.auth({ guards: ['jwt'] }). Authenticated requests expose the
validated access claims as ctx.jwtClaims.
Generated config/jwt.ts
The default is Ed25519, 10-minute access tokens, 30-day non-sliding refresh tokens, bearer transport, and a public JWKS route:
const jwtConfig = defineJwtConfig({
issuer: env.get('JWT_ISSUER'),
audience: env.get('JWT_AUDIENCE'),
access: {
algorithm: 'EdDSA',
expiresIn: '10m',
clockTolerance: '30s',
},
refresh: {
expiresIn: '30d',
sliding: false,
reuseGraceMs: 2_000,
table: 'jwt_refresh_tokens',
prefix: 'rt_',
tokenSecretLength: 40,
},
keys: keyDrivers.env({ privateKeyPem, publicKeyPem, kid: env.get('JWT_KID') }),
jwks: {
enabled: true,
route: '/.well-known/jwks.json',
cacheMaxAge: 3_600,
},
transport: transports.bearer(),
denylist: { enabled: false },
})Durations accept seconds, minutes, hours, days, weeks, and years, including decimals such as
1.5h. Set clock tolerance only high enough for measured clock skew.
Algorithms and key drivers
| Mode | Configuration | Signing | JWKS output |
|---|---|---|---|
| EdDSA / ES256 / PS256 / RS256 | keyDrivers.env({ privateKeyPem, publicKeyPem, kid }) |
Yes | Public key |
| HS256 | keyDrivers.env({ secret, kid }) |
Yes | Empty; symmetric secrets are never published |
| Encrypted database | keyDrivers.database({ table }) |
Yes | Active and overlapping public keys |
| External identity provider | keyDrivers.remoteJwks({ url, ...cacheOptions }) |
No | Cached public remote keys |
HS256 secrets must contain at least 32 bytes and must be generated independently from APP_KEY.
Remote JWKS mode is verification-only: login and token issuance intentionally fail.
Transport
Bearer mode reads only a strict Authorization: Bearer <token> header. Cookie mode is for
first-party browser clients and requires the CSRF contract documented in Browser cookies and
CSRF.
Optional immediate revocation
Without a denylist, logout stops renewal while an access JWT remains valid until its short expiry.
Pass MemoryDenylist for one-process development or CacheDenylist with a shared Adonis Cache
backend in production. Entries contain only the jti and expire with the token.