Session Manager API
Page summary:The Session Manager API is available at
strapi.sessionManager. Use it from the backend or a plugin to issue short-lived access tokens and longer-lived refresh tokens, scoped to an origin such asadminorusers-permissions.
The Session Manager powers admin panel session management and Users & Permissions refresh-token mode. HTTP endpoints for those products are documented on their own pages. This page describes the JavaScript API for custom controllers, services, and plugins.
Access tokens are JWTs. Send them as Authorization: Bearer <token>. Refresh tokens are also JWTs. Admin stores refresh tokens in an HTTP-only cookie. Users & Permissions can return them in the response body or in a cookie, depending on configuration.
End-user session list and revoke flows use the Users & Permissions REST API. Admin users manage devices from the admin profile, not from this API.
Access the API
strapi.sessionManager is registered at startup. Call it with an origin name to get an origin-scoped manager:
const adminSessions = strapi.sessionManager('admin');
const upSessions = strapi.sessionManager('users-permissions');
The origin string must already be registered with defineOrigin(). Strapi registers admin and users-permissions during bootstrap. A missing origin throws:
SessionManager: Origin '<name>' is not defined. Please define it using defineOrigin('<name>', config).
Origins and configuration
Each origin has its own JWT keys and lifespans. Strapi stores every origin's rows in the hidden admin::session content-type (database table strapi_sessions). Rows are isolated by the origin field.
| Origin | Registered by | Typical config |
|---|---|---|
admin | Admin server bootstrap | admin.auth.sessions and admin.auth.secret |
users-permissions | Users & Permissions bootstrap | plugin::users-permissions session keys |
defineOrigin() accepts the following fields:
| Field | Type | Description |
|---|---|---|
jwtSecret | string | Symmetric signing secret. Required for HS256 (the default algorithm). |
accessTokenLifespan | number | Access token lifetime, in seconds. |
maxRefreshTokenLifespan | number | Maximum lifetime of a refresh-token family, in seconds. |
idleRefreshTokenLifespan | number | Idle timeout for type: 'refresh' tokens, in seconds. |
maxSessionLifespan | number | Maximum lifetime of a type: 'session' family, in seconds. |
idleSessionLifespan | number | Idle timeout for type: 'session' tokens, in seconds. |
algorithm | string | JWT algorithm. Default: HS256. |
jwtOptions | object | Extra options passed to jsonwebtoken. For RS*, ES*, and PS* algorithms, set privateKey (signing) and publicKey (verification). |
Asymmetric algorithms read keys from jwtOptions.privateKey and jwtOptions.publicKey. They do not use jwtSecret.
Token types
generateRefreshToken() accepts type: 'refresh' (default) or type: 'session'. The type selects which idle and max lifespans apply:
type | Idle lifespan | Max lifespan |
|---|---|---|
refresh | idleRefreshTokenLifespan | maxRefreshTokenLifespan |
session | idleSessionLifespan | maxSessionLifespan |
Admin uses refresh when rememberMe is true and session otherwise. Both types still issue a JWT whose payload type is 'refresh'. Access tokens use payload type: 'access'.
Session records
Active sessions are database rows. Typical fields include:
| Field | Description |
|---|---|
userId | User identifier stored as a string. |
sessionId | Opaque id embedded in the refresh JWT. |
deviceId | Optional device family. Used for targeted invalidation. |
origin | Origin that created the row. |
type | 'refresh' or 'session'. |
status | 'active', 'rotated', or 'revoked'. |
metadata | Origin-defined object. The Session Manager stores it as-is and does not interpret it. |
expiresAt | Idle expiry. |
absoluteExpiresAt | Family expiry. Rotation copies this value to the child row. |
childId | Session id of the rotated successor, when present. |
rotateRefreshToken() marks the previous row as rotated and creates a child. listSessions() returns only status: 'active' rows, so each login family appears once.
Expired rows are deleted in batches about every 50 Session Manager calls. isSessionActive() also deletes a row that has already expired.
Method overview
Call methods on an origin-scoped manager, except defineOrigin(), hasOrigin(), and generateSessionId(), which live on strapi.sessionManager itself.
| Method | Purpose |
|---|---|
generateRefreshToken() | Create a session row and a refresh JWT. |
generateAccessToken() | Issue an access JWT from a valid refresh JWT. |
rotateRefreshToken() | Replace a refresh JWT and keep the same family expiry. |
validateAccessToken() | Verify an access JWT (synchronous). |
validateRefreshToken() | Verify a refresh JWT and the backing session row. |
invalidateRefreshToken() | Delete sessions for a user, optionally limited to one device. |
listSessions() | List active sessions for a user. |
revokeSessionById() | Delete one session owned by the user and origin. |
isSessionActive() | Return whether a session exists and is not expired. |
defineOrigin() | Register origin configuration (root API). |
hasOrigin() | Check whether an origin is registered (root API). |
generateSessionId() | Generate a random session id (root API). |
Origin methods
The following methods are called on strapi.sessionManager('<origin>').
generateRefreshToken()
Creates a session row, then signs a refresh JWT that includes userId, sessionId, type: 'refresh', iat, and exp.
strapi.sessionManager(origin).generateRefreshToken()generateRefreshToken()
Creates a session and returns a refresh JWT. The origin must already be defined.
undefined when the origin does not track devices.refresh.- Request
const sessions = strapi.sessionManager('users-permissions');
const { token, sessionId, absoluteExpiresAt } = await sessions.generateRefreshToken(
String(user.id),
deviceId,
{
type: 'refresh',
metadata: { deviceName: 'CLI' },
}
);
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sessionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"absoluteExpiresAt": "2026-09-26T12:00:00.000Z"
}
generateAccessToken()
Validates the refresh JWT and the active session row, then signs a short-lived access JWT. On failure the return value is { error: 'invalid_refresh_token' } instead of throwing.
strapi.sessionManager(origin).generateAccessToken()generateAccessToken()
Issues an access JWT from a refresh JWT. The session must be active and within idle and absolute expiry.
generateRefreshToken() or rotateRefreshToken().- Request
const result = await strapi.sessionManager('admin').generateAccessToken(refreshToken);
if ('error' in result) {
throw new Error(result.error);
}
const accessToken = result.token;
- 200 OK
- 400 invalid_refresh_token
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
{
"error": "invalid_refresh_token"
}
rotateRefreshToken()
Creates a child session, marks the current row as rotated, and returns a new refresh JWT. Idle and max windows are enforced against the current row. If the parent already has a childId, the same child token is returned again.
strapi.sessionManager(origin).rotateRefreshToken()rotateRefreshToken()
Rotates a refresh JWT. Copies deviceId and metadata onto the child. Keeps the original absoluteExpiresAt.
- Request
const rotated = await strapi.sessionManager('users-permissions').rotateRefreshToken(
refreshToken
);
if ('error' in rotated) {
throw new Error(rotated.error);
}
- 200 OK
- 400 error
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sessionId": "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5",
"absoluteExpiresAt": "2026-09-26T12:00:00.000Z",
"type": "refresh"
}
{
"error": "invalid_refresh_token"
}
rotateRefreshToken() can also return { error: 'idle_window_elapsed' } or { error: 'max_window_elapsed' }.
validateAccessToken()
Verifies the JWT signature, algorithm, and payload type: 'access'. This method is synchronous and does not read the database. A revoked session can still present a valid access token until that token expires. Pair it with isSessionActive() when you need the row to still exist.
strapi.sessionManager(origin).validateAccessToken()validateAccessToken()
Verifies an access JWT for the origin. Returns the payload when the token is valid.
- Request
const result = strapi.sessionManager('admin').validateAccessToken(accessToken);
if (!result.isValid) {
return;
}
const { userId, sessionId } = result.payload;
- 200 OK
- 401 invalid
{
"isValid": true,
"payload": {
"userId": "1",
"sessionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"type": "access",
"iat": 1756281600,
"exp": 1756283400
}
}
{
"isValid": false,
"payload": null
}
validateRefreshToken()
Verifies the refresh JWT, then loads the session row. The row must exist, belong to the same userId, have status: 'active', and be within expiresAt and absoluteExpiresAt.
strapi.sessionManager(origin).validateRefreshToken()validateRefreshToken()
Validates a refresh JWT against the origin configuration and the session row.
- Request
const validation = await strapi.sessionManager('admin').validateRefreshToken(
refreshToken
);
if (!validation.isValid) {
return;
}
- 200 OK
- 401 invalid
{
"isValid": true,
"userId": "1",
"sessionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
}
{
"isValid": false
}
invalidateRefreshToken()
Deletes session rows for the origin and user. Pass deviceId to limit deletion to that device family. Omit it to delete every session for the user on this origin.
strapi.sessionManager(origin).invalidateRefreshToken()invalidateRefreshToken()
Deletes matching session rows. Used after logout and after credential changes.
- Request
await strapi.sessionManager('users-permissions').invalidateRefreshToken(
String(user.id)
);
await strapi.sessionManager('users-permissions').invalidateRefreshToken(
String(user.id),
deviceId
);
{}
listSessions()
Returns active sessions for the user and origin, newest first.
strapi.sessionManager(origin).listSessions()listSessions()
Lists rows with status active. Rotated and revoked rows are omitted.
- Request
const sessions = await strapi.sessionManager('admin').listSessions(String(user.id));
[
{
"userId": "1",
"sessionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"deviceId": "3f2e1d0c-9b8a-7c6d-5e4f-3210fedcba98",
"origin": "admin",
"type": "refresh",
"status": "active",
"metadata": { "deviceName": "Chrome on macOS" },
"expiresAt": "2026-09-09T12:00:00.000Z",
"absoluteExpiresAt": "2026-09-26T12:00:00.000Z"
}
]
revokeSessionById()
Deletes one session when the row belongs to the given user and origin. Returns true if a matching row was deleted.
strapi.sessionManager(origin).revokeSessionById()revokeSessionById()
Revokes a single session. Returns false when the row is missing or owned by another user or origin.
- Request
const revoked = await strapi.sessionManager('admin').revokeSessionById(
String(user.id),
sessionId
);
true
isSessionActive()
Returns true when a row exists for the origin and expiresAt is still in the future. If the row exists but has expired, the method deletes it and returns false.
strapi.sessionManager(origin).isSessionActive()isSessionActive()
Checks that a session row exists, matches the origin, and has not expired.
- Request
const access = strapi.sessionManager('admin').validateAccessToken(accessToken);
if (!access.isValid) {
return;
}
const active = await strapi.sessionManager('admin').isSessionActive(
access.payload.sessionId
);
true
Root methods
The following methods are called on strapi.sessionManager without an origin argument.
defineOrigin()
Registers JWT and lifespan configuration for an origin. Call this during plugin bootstrap before issuing tokens. Calling it again with the same origin name replaces the previous configuration.
strapi.sessionManager.defineOrigin('my-plugin', {
jwtSecret: strapi.config.get('plugin::my-plugin.jwtSecret'),
accessTokenLifespan: 30 * 60,
maxRefreshTokenLifespan: 30 * 24 * 60 * 60,
idleRefreshTokenLifespan: 14 * 24 * 60 * 60,
maxSessionLifespan: 24 * 60 * 60,
idleSessionLifespan: 2 * 60 * 60,
algorithm: 'HS256',
});
hasOrigin()
Returns whether defineOrigin() has been called for the name.
if (!strapi.sessionManager.hasOrigin('my-plugin')) {
throw new Error('Session origin my-plugin is not configured');
}
generateSessionId()
Returns a 32-character hex string. generateRefreshToken() already calls this internally.
const sessionId = strapi.sessionManager.generateSessionId();
Custom origin example
The following plugin bootstrap registers an origin and issues tokens for a custom user id:
module.exports = {
async bootstrap({ strapi }) {
strapi.sessionManager.defineOrigin('my-plugin', {
jwtSecret: strapi.config.get('plugin::my-plugin.jwtSecret'),
accessTokenLifespan: 30 * 60,
maxRefreshTokenLifespan: 30 * 24 * 60 * 60,
idleRefreshTokenLifespan: 14 * 24 * 60 * 60,
maxSessionLifespan: 24 * 60 * 60,
idleSessionLifespan: 2 * 60 * 60,
});
},
};
const origin = strapi.sessionManager('my-plugin');
const { token: refreshToken } = await origin.generateRefreshToken(
userId,
deviceId,
{ type: 'refresh' }
);
const access = await origin.generateAccessToken(refreshToken);
admin.auth.secret is still required at startup when the admin panel is served. API-only apps can set serveAdminPanel: false so that check is skipped. Users & Permissions can reuse admin.auth.secret when jwtSecret is unset.