Skip to content

Token lifecycle

Implement login, authentication, refresh rotation, replay response, and logout.

Login

Verify credentials with your user model, then ask the JWT guard to issue a pair:

const user = await User.verifyCredentials(email, password)
const tokens = await auth.use('jwt').login(user)

The response contains tokenType, accessToken, expiresIn, refreshToken, and refreshExpiresIn. Access tokens are signed JWTs. Refresh tokens are opaque, high-entropy values; only their hashes are stored.

Authenticate

const user = await auth.use('jwt').authenticate()
const claims = ctx.jwtClaims

Verification requires the configured algorithm, issuer, audience, kid, subject, jti, issued-at, not-before, and expiration claims. An expired access token raises E_TOKEN_EXPIRED; every other invalid signature or claim fails as unauthorized.

Refresh

const next = await auth.use('jwt').refresh(request.input('refreshToken'))

Every successful refresh consumes the presented token and returns a new access token plus a new refresh token in the same family. With sliding: false, the family keeps its original expiration; with sliding: true, each successful rotation receives the configured refresh TTL.

Concurrent clients may retry inside reuseGraceMs; the stale token is rejected without treating that short race as an attack. After the grace period, reuse raises E_REFRESH_TOKEN_REUSE, revokes every token in the family, emits jwt:refresh_reuse_detected, and creates a jwt.reuse_detected span. The client must discard the session and require credentials again.

Logout

Protect logout with JWT authentication so the current access claims are available:

await auth.use('jwt').authenticate()
await auth.use('jwt').logout(request.input('refreshToken'))

This revokes the supplied refresh token, optionally denylists the current access-token jti, and clears cookie transport state.

To end every session for a user:

await auth.use('jwt').logoutAll(auth.use('jwt').getUserOrFail())

Client behavior

  • Keep bearer access tokens in memory, not local storage, when possible.
  • Replace the stored refresh token atomically after every refresh.
  • Never retry a refresh indefinitely. Reuse detection is a security event, not a transient error.
  • Treat 401 responses as session invalidation. Re-authenticate rather than weakening claim checks.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close