This guide covers the complete lifecycle of sessions in Ory Kratos, from creation during login to revocation during logout.
Session creation
Sessions are created when a user successfully completes authentication.
Creation flow
Authentication completed
User completes a self-service flow (login, registration) or authenticates via Admin API.
Session activation
The session manager activates the session: func ( m * Manager ) ActivateSession (
r * http . Request ,
session * Session ,
i * identity . Identity ,
authenticatedAt time . Time ,
) error {
session . Active = true
session . AuthenticatedAt = authenticatedAt
session . IssuedAt = authenticatedAt
session . Identity = i
session . IdentityID = i . ID
session . SetAuthenticatorAssuranceLevel ()
session . SetSessionDeviceInformation ( r )
return nil
}
Session persisted
The session is stored in the database with a unique token.
Cookie/token issued
For browsers, a cookie is set. For API clients, the token is returned in the response.
Creating inactive sessions
For partial authentication (before completing MFA):
func NewInactiveSession () * Session {
return & Session {
ID : uuid . Nil ,
Token : x . OrySessionToken + randx . MustString ( 32 , randx . AlphaNum ),
LogoutToken : x . OryLogoutToken + randx . MustString ( 32 , randx . AlphaNum ),
Active : false ,
AuthenticatorAssuranceLevel : identity . NoAuthenticatorAssuranceLevel ,
}
}
Session validation
Sessions are validated on every authenticated request.
Validation steps
Extract token
Token is extracted from cookie, Authorization header, or X-Session-Token header.
Database lookup
Session is retrieved from the database using the token.
Active check
Session must be active, not expired, and belong to an active identity: func ( s * Session ) IsActive () bool {
return s . Active &&
s . ExpiresAt . After ( time . Now ()) &&
( s . Identity == nil || s . Identity . IsActive ())
}
AAL check (optional)
If an endpoint requires specific AAL, verify the session satisfies it.
Fetch from request
func ( m * Manager ) FetchFromRequest ( ctx context . Context , r * http . Request ) ( * Session , error ) {
// Try cookie first
token , err := m . extractTokenFromCookie ( r )
if err != nil {
// Try Authorization header
token , err = m . extractTokenFromAuthHeader ( r )
if err != nil {
// Try X-Session-Token header
token , err = m . extractTokenFromCustomHeader ( r )
if err != nil {
return nil , NewErrNoCredentialsForSession ()
}
}
}
session , err := m . persister . GetSessionByToken ( ctx , token )
if err != nil {
return nil , err
}
if ! session . IsActive () {
return nil , NewErrNoActiveSessionFound ()
}
return session , nil
}
AAL validation
Check if a session satisfies required AAL:
func ( m * Manager ) DoesSessionSatisfy (
ctx context . Context ,
sess * Session ,
matcher string ,
opts ... ManagerOptions ,
) error {
switch matcher {
case "aal1" :
if sess . AuthenticatorAssuranceLevel >= AuthenticatorAssuranceLevel1 {
return nil
}
case "aal2" :
if sess . AuthenticatorAssuranceLevel >= AuthenticatorAssuranceLevel2 {
return nil
}
case "highest_available" :
availableAAL := sess . Identity . InternalAvailableAAL
if sess . AuthenticatorAssuranceLevel >= availableAAL . ToAAL () {
return nil
}
}
return NewErrAALNotSatisfied ( redirectURL )
}
The highest_available matcher requires users to authenticate with their highest available AAL. If a user has TOTP configured but only authenticated with password, they’ll be prompted for the second factor.
Session extension
Sessions can be extended to prevent expiration.
Automatic extension
Sessions are automatically extended on successful validation:
func ( m * Manager ) RefreshCookie (
ctx context . Context ,
w http . ResponseWriter ,
r * http . Request ,
s * Session ,
) error {
if ! s . CanBeRefreshed ( ctx , m . r . Config ()) {
return nil
}
s . Refresh ( ctx , m . r . Config ())
if err := m . r . SessionPersister (). UpsertSession ( ctx , s ); err != nil {
return err
}
return m . IssueCookie ( ctx , w , r , s )
}
Manual extension via Admin API
Extend a session explicitly:
curl -X PATCH "https://kratos-admin/admin/sessions/{id}/extend"
Response:
Refresh window
To reduce database writes, sessions are only refreshed when approaching expiration:
session :
lifespan : 720h # 30 days
earliest_possible_extend : 1h # Only extend if < 1h left
Sessions are extended by resetting expires_at to now + lifespan. Setting earliest_possible_extend too low causes excessive database writes.
Session upgrades (MFA)
Sessions can be upgraded from AAL1 to AAL2.
MFA flow
Initial authentication
User authenticates with password (AAL1 session created).
MFA required
Application requires AAL2, user is redirected to complete second factor.
Second factor completed
User completes TOTP/WebAuthn challenge.
Session upgraded
Authentication method is added to session: func ( m * Manager ) SessionAddAuthenticationMethods (
ctx context . Context ,
sid uuid . UUID ,
methods ... AuthenticationMethod ,
) error {
session , err := m . r . SessionPersister (). GetSession ( ctx , sid )
if err != nil {
return err
}
for _ , method := range methods {
session . CompletedLoginForMethod ( method )
}
session . SetAuthenticatorAssuranceLevel ()
session . AuthenticatedAt = time . Now (). UTC ()
return m . r . SessionPersister (). UpsertSession ( ctx , session )
}
AAL errors
When AAL is insufficient:
{
"error" : {
"id" : "session_aal2_required" ,
"code" : 403 ,
"status" : "Forbidden" ,
"reason" : "Session does not fulfill the requested Authenticator Assurance Level" ,
"details" : {
"redirect_browser_to" : "https://example.com/login?aal=aal2&return_to=/protected"
}
}
}
Session revocation
Sessions can be revoked (logged out) in several ways.
Self-service logout
Users can revoke their own sessions:
Revoke current session
Revoke specific session
curl -X DELETE "https://kratos-public/sessions" \
--cookie "ory_kratos_session=..."
Users cannot revoke their current session via the DELETE /sessions/{id} endpoint. Use the logout flow instead.
Admin revocation
Deactivate session
Delete all identity sessions
curl -X DELETE "https://kratos-admin/admin/sessions/{id}"
Revocation types
Method Effect Data retention Deactivate Sets active = false Session data retained Delete Removes from database Session data deleted
Bulk revocation
Revoke all sessions except current:
curl -X DELETE "https://kratos-public/sessions" \
--cookie "ory_kratos_session=..."
Response:
Session persistence
Database storage
Sessions are stored in the sessions table:
CREATE TABLE sessions (
id UUID PRIMARY KEY ,
nid UUID NOT NULL ,
identity_id UUID NOT NULL ,
token VARCHAR ( 255 ) UNIQUE NOT NULL ,
logout_token VARCHAR ( 255 ) UNIQUE NOT NULL ,
active BOOLEAN NOT NULL DEFAULT TRUE,
expires_at TIMESTAMP NOT NULL ,
authenticated_at TIMESTAMP NOT NULL ,
issued_at TIMESTAMP NOT NULL ,
aal VARCHAR ( 10 ) NOT NULL DEFAULT 'aal0' ,
authentication_methods JSONB,
created_at TIMESTAMP NOT NULL ,
updated_at TIMESTAMP NOT NULL
);
Session devices
Device information is stored separately:
CREATE TABLE session_devices (
id UUID PRIMARY KEY ,
session_id UUID NOT NULL ,
identity_id UUID,
nid UUID NOT NULL ,
ip_address VARCHAR ( 50 ),
user_agent TEXT ,
location VARCHAR ( 255 ),
created_at TIMESTAMP NOT NULL ,
updated_at TIMESTAMP NOT NULL ,
FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE
);
Session listing
List all sessions (Admin)
curl "https://kratos-admin/admin/sessions?page_size=100&active=true"
Query parameters:
page_size: Results per page
page_token: Pagination token
active: Filter by active status
expand: Include identity and/or devices
List identity sessions
curl "https://kratos-admin/admin/identities/{id}/sessions?active=true"
List my sessions (self-service)
curl "https://kratos-public/sessions" \
--cookie "ory_kratos_session=..."
The self-service endpoint excludes the current session from results. Use /sessions/whoami to get the current session.
Session expandables
Control which associations are loaded:
curl "https://kratos-admin/admin/sessions/{id}?expand=identity&expand=devices"
Available expandables:
identity: Include full identity object
devices: Include device history
Session optimization tips:
Use longer earliest_possible_extend to reduce database writes
Enable session caching for high-traffic read operations
Paginate session lists with keyset pagination
Regularly clean up expired sessions
Use database indexes on token, identity_id, and expires_at
Session cleanup
Kratos automatically removes expired sessions based on configuration. You can also manually trigger cleanup via housekeeping jobs.