Skip to main content

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 as admin or users-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.

Built-in HTTP APIs

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.

OriginRegistered byTypical config
adminAdmin server bootstrapadmin.auth.sessions and admin.auth.secret
users-permissionsUsers & Permissions bootstrapplugin::users-permissions session keys

defineOrigin() accepts the following fields:

FieldTypeDescription
jwtSecretstringSymmetric signing secret. Required for HS256 (the default algorithm).
accessTokenLifespannumberAccess token lifetime, in seconds.
maxRefreshTokenLifespannumberMaximum lifetime of a refresh-token family, in seconds.
idleRefreshTokenLifespannumberIdle timeout for type: 'refresh' tokens, in seconds.
maxSessionLifespannumberMaximum lifetime of a type: 'session' family, in seconds.
idleSessionLifespannumberIdle timeout for type: 'session' tokens, in seconds.
algorithmstringJWT algorithm. Default: HS256.
jwtOptionsobjectExtra 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:

typeIdle lifespanMax lifespan
refreshidleRefreshTokenLifespanmaxRefreshTokenLifespan
sessionidleSessionLifespanmaxSessionLifespan

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:

FieldDescription
userIdUser identifier stored as a string.
sessionIdOpaque id embedded in the refresh JWT.
deviceIdOptional device family. Used for targeted invalidation.
originOrigin that created the row.
type'refresh' or 'session'.
status'active', 'rotated', or 'revoked'.
metadataOrigin-defined object. The Session Manager stores it as-is and does not interpret it.
expiresAtIdle expiry.
absoluteExpiresAtFamily expiry. Rotation copies this value to the child row.
childIdSession 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.

MethodPurpose
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.

Parameters
userId
string
required
User identifier stored on the session row.
deviceId
string or undefined
optional
Optional device family. Pass undefined when the origin does not track devices.
options.type
'refresh' | 'session'
optional
Selects idle and max lifespans. Default: refresh.
options.metadata
object
optional
Free-form data persisted on the row (for example device label).
const sessions = strapi.sessionManager('users-permissions');

const { token, sessionId, absoluteExpiresAt } = await sessions.generateRefreshToken(
String(user.id),
deviceId,
{
type: 'refresh',
metadata: { deviceName: 'CLI' },
}
);
200 OK
{
"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.

Parameters
refreshToken
string
required
Refresh JWT returned by generateRefreshToken() or rotateRefreshToken().
const result = await strapi.sessionManager('admin').generateAccessToken(refreshToken);

if ('error' in result) {
throw new Error(result.error);
}

const accessToken = result.token;
200 OK
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

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.

Parameters
refreshToken
string
required
Current refresh JWT.
const rotated = await strapi.sessionManager('users-permissions').rotateRefreshToken(
refreshToken
);

if ('error' in rotated) {
throw new Error(rotated.error);
}
200 OK
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sessionId": "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5",
"absoluteExpiresAt": "2026-09-26T12:00:00.000Z",
"type": "refresh"
}
Rotation errors

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.

Parameters
token
string
required
Access JWT.
const result = strapi.sessionManager('admin').validateAccessToken(accessToken);

if (!result.isValid) {
return;
}

const { userId, sessionId } = result.payload;
200 OK
{
"isValid": true,
"payload": {
"userId": "1",
"sessionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"type": "access",
"iat": 1756281600,
"exp": 1756283400
}
}

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.

Parameters
token
string
required
Refresh JWT.
const validation = await strapi.sessionManager('admin').validateRefreshToken(
refreshToken
);

if (!validation.isValid) {
return;
}
200 OK
{
"isValid": true,
"userId": "1",
"sessionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
}

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.

Parameters
userId
string
required
User identifier.
deviceId
string
optional
When set, only rows with this device id are deleted.
await strapi.sessionManager('users-permissions').invalidateRefreshToken(
String(user.id)
);

await strapi.sessionManager('users-permissions').invalidateRefreshToken(
String(user.id),
deviceId
);
200 OK
{}

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.

Parameters
userId
string
required
User identifier.
const sessions = await strapi.sessionManager('admin').listSessions(String(user.id));
200 OK
[
{
"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.

Parameters
userId
string
required
User identifier that must own the row.
sessionId
string
required
Session id to delete.
const revoked = await strapi.sessionManager('admin').revokeSessionById(
String(user.id),
sessionId
);
200 OK
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.

Parameters
sessionId
string
required
Session id from an access or refresh token payload.
const access = strapi.sessionManager('admin').validateAccessToken(accessToken);

if (!access.isValid) {
return;
}

const active = await strapi.sessionManager('admin').isSessionActive(
access.payload.sessionId
);
200 OK
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.

What's next?

Was this page helpful?