Sessions
Manage user sessions, tokens, and authentication state.
Overview
Sessions track authenticated users across requests. Transactional Auth manages sessions with configurable security policies.
Session Types
| Type | Storage | Use Case |
|---|---|---|
| Token-based | Client-side | SPAs, Mobile apps |
| Cookie-based | Server-side | Server-rendered apps |
Token Structure
Access Token
Short-lived token for API requests:
{
"iss": "https://auth.usetransactional.com",
"sub": "user_xxx",
"aud": "your_client_id",
"exp": 1704067200,
"iat": 1704063600,
"scope": "openid profile email",
"azp": "your_client_id"
}Refresh Token
Long-lived token for obtaining new access tokens:
- Stored securely server-side or in secure storage
- Rotated on each use (theft detection)
- Can be revoked to end session
ID Token
Contains user identity claims:
{
"iss": "https://auth.usetransactional.com",
"sub": "user_xxx",
"aud": "your_client_id",
"exp": 1704067200,
"iat": 1704063600,
"nonce": "random-nonce",
"email": "user@example.com",
"email_verified": true,
"name": "John Doe",
"picture": "https://example.com/avatar.jpg"
}Managing Sessions
List User Sessions
TypeScript:
const sessions = await client.auth.sessions.list('user_xxx');
for (const session of sessions) {
console.log(`Session: ${session.id}`);
console.log(`Created: ${session.createdAt}`);
console.log(`Last Active: ${session.lastActiveAt}`);
console.log(`IP: ${session.ipAddress}`);
console.log(`User Agent: ${session.userAgent}`);
}Python:
sessions = client.auth.sessions.list("user_xxx")
for session in sessions:
print(f"Session: {session.id}")
print(f"IP: {session.ip_address}")Get Current Session
const session = await client.auth.sessions.getCurrent(accessToken);
console.log('Current session:', session.id);Revoke a Session
// Revoke specific session
await client.auth.sessions.revoke('session_xxx');Revoke All Sessions
// Log out user from all devices
await client.auth.sessions.revokeAll('user_xxx');Revoke by Refresh Token
await client.auth.tokens.revoke(refreshToken);Token Refresh
Refresh access tokens before they expire:
async function refreshTokens(refreshToken: string) {
const response = await fetch('https://auth.usetransactional.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'your_client_id',
refresh_token: refreshToken,
}),
});
const tokens = await response.json();
// IMPORTANT: Use the new refresh token
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token, // New token!
};
}Automatic Token Refresh
class TokenManager {
private accessToken: string;
private refreshToken: string;
private expiresAt: number;
async getAccessToken(): Promise<string> {
// Refresh if expiring within 5 minutes
if (Date.now() > this.expiresAt - 300000) {
await this.refresh();
}
return this.accessToken;
}
private async refresh() {
const tokens = await refreshTokens(this.refreshToken);
this.accessToken = tokens.accessToken;
this.refreshToken = tokens.refreshToken;
this.expiresAt = Date.now() + 3600000; // 1 hour
}
}Session Policies
Configure session behavior per organization.
Concurrent Sessions
Limit active sessions per user:
await client.auth.security.update({
sessions: {
maxConcurrent: 3, // Max 3 simultaneous sessions
},
});When limit is reached:
- New login succeeds
- Oldest session is automatically revoked
Idle Timeout
Revoke sessions after inactivity:
await client.auth.security.update({
sessions: {
idleTimeout: 60, // Minutes
},
});Absolute Timeout
Maximum session duration regardless of activity:
await client.auth.security.update({
sessions: {
absoluteTimeout: 480, // 8 hours max
},
});Token Validation
Validate Access Token
async function validateToken(token: string) {
try {
const response = await fetch('https://auth.usetransactional.com/token/introspection', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(`${clientId}:${clientSecret}`)}`,
},
body: new URLSearchParams({ token }),
});
const result = await response.json();
return result.active;
} catch {
return false;
}
}Validate Locally (JWT)
For better performance, validate JWTs locally:
import jwt from 'jsonwebtoken';
const jwks = await fetchJWKS('https://auth.usetransactional.com/.well-known/jwks.json');
function validateLocally(token: string) {
try {
const decoded = jwt.verify(token, getPublicKey(jwks), {
algorithms: ['RS256'],
audience: 'your_client_id',
issuer: 'https://auth.usetransactional.com',
});
return { valid: true, claims: decoded };
} catch (error) {
return { valid: false, error: error.message };
}
}Session Information
Track session metadata:
| Field | Description |
|---|---|
ipAddress | IP address at login |
userAgent | Browser/client info |
location | Geo-location (if available) |
deviceType | Desktop, mobile, tablet |
createdAt | Session start time |
lastActiveAt | Last activity time |
expiresAt | Session expiration |
const session = await client.auth.sessions.get('session_xxx');
console.log('Device:', session.deviceType);
console.log('Location:', session.location?.city);
console.log('Created:', session.createdAt);Logout Implementation
Single Device Logout
app.post('/logout', async (req, res) => {
// Revoke current refresh token
await client.auth.tokens.revoke(req.session.refreshToken);
// Clear server-side session
req.session.destroy();
// Redirect to home
res.redirect('/');
});All Devices Logout
app.post('/logout-all', async (req, res) => {
const userId = req.user.sub;
// Revoke all sessions
await client.auth.sessions.revokeAll(userId);
// Clear current session
req.session.destroy();
res.redirect('/');
});Federated Logout (SSO)
app.get('/logout', async (req, res) => {
// Revoke local session
await client.auth.tokens.revoke(req.session.refreshToken);
req.session.destroy();
// Redirect to IdP logout
const logoutUrl = `https://auth.usetransactional.com/logout?` + new URLSearchParams({
client_id: 'your_client_id',
post_logout_redirect_uri: 'https://yourapp.com',
});
res.redirect(logoutUrl);
});Session Security
Refresh Token Rotation
Tokens are rotated on each refresh. If a stolen token is used:
- Valid token is used first → New tokens issued
- Stolen token is used → Detected as replay
- All tokens for that session are revoked
Token Theft Detection
// When refresh fails due to rotation
if (error.code === 'TOKEN_REUSE_DETECTED') {
// Possible token theft - force re-authentication
await client.auth.sessions.revokeAll(userId);
redirectToLogin();
}Next Steps
On This Page
- Overview
- Session Types
- Token Structure
- Access Token
- Refresh Token
- ID Token
- Managing Sessions
- List User Sessions
- Get Current Session
- Revoke a Session
- Revoke All Sessions
- Revoke by Refresh Token
- Token Refresh
- Automatic Token Refresh
- Session Policies
- Concurrent Sessions
- Idle Timeout
- Absolute Timeout
- Token Validation
- Validate Access Token
- Validate Locally (JWT)
- Session Information
- Logout Implementation
- Single Device Logout
- All Devices Logout
- Federated Logout (SSO)
- Session Security
- Refresh Token Rotation
- Token Theft Detection
- Next Steps