---
title: "Token lifecycle"
description: "Implement login, authentication, refresh rotation, replay response, and logout."
image: "https://adonisjs-jwt.pages.dev/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://adonisjs-jwt.pages.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Token lifecycle

## Login

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

```ts
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

```ts
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

```ts
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:

```ts
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:

```ts
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.

Source: https://adonisjs-jwt.pages.dev/lifecycle/index.mdx
