Sessions & Authentication
Sessions provide secure user authentication and authorization for your Ductape application. Create JWT-based sessions with encrypted data, verify tokens, and handle authentication flows.
If your client is configured with a publishable key (browser/frontend), session methods are not available—calling sessions.start, sessions.verify, sessions.refresh, sessions.revoke, etc. will throw. Sessions can only be started and managed on your backend (e.g. BFF or API with an access key). Your backend issues a session token and returns it to the frontend; the frontend must then include that token as the session property in every Ductape request (e.g. databases.query({ table: 'x', session: token })). The proxy rejects requests that omit session. The examples on this page apply when using an access key (e.g. server-side or backend-only client).
Starting a Session
Basic Session Start
import { Ductape } from '@ductape/client';
const ductape = new Ductape({
accessKey: 'your-access-key',
product: 'your-product',
env: 'prd'
});
const session = await ductape.sessions.start({
tag: 'user-session',
data: {
userId: 'user-123',
email: 'user@example.com',
role: 'user'
}
});
console.log('Session token:', session.token);
console.log('Refresh token:', session.refreshToken);
// Store tokens securely
localStorage.setItem('ductape_token', session.token);
localStorage.setItem('ductape_refresh', session.refreshToken);
Session with Custom Duration
const session = await ductape.sessions.start({
tag: 'admin-session',
data: {
userId: 'admin-456',
email: 'admin@example.com',
role: 'admin',
permissions: ['read', 'write', 'delete']
},
expiresIn: 86400 // 24 hours in seconds
});
Different Session Types
Use different tags for different authentication flows:
// Checkout flow session
const checkoutSession = await ductape.sessions.start({
tag: 'checkout-session',
data: {
userId: 'user-123',
cartId: 'cart-789',
shippingAddress: '123 Main St'
},
expiresIn: 1800 // 30 minutes
});
// Support chat session
const supportSession = await ductape.sessions.start({
tag: 'support-chat',
data: {
userId: 'user-123',
ticketId: 'ticket-456',
agent: 'agent-789'
}
});
Verifying Sessions
Verify Token
const token = localStorage.getItem('ductape_token');
try {
const result = await ductape.sessions.verify({
tag: 'user-session',
token: token
});
console.log('Session valid');
console.log('Decrypted data:', result.data);
// Access encrypted session data
console.log('User ID:', result.data.userId);
console.log('Email:', result.data.email);
console.log('Role:', result.data.role);
} catch (error) {
console.log('Session invalid or expired');
// Redirect to login
window.location.href = '/login';
}
Auto-verify on App Load
async function initializeAuth() {
const token = localStorage.getItem('ductape_token');
if (!token) {
// No token, show login
showLoginPage();
return;
}
try {
const result = await ductape.sessions.verify({
tag: 'user-session',
token
});
// Token valid, load app with decrypted session data
loadApp(result.data);
} catch (error) {
// Token invalid, clear and show login
localStorage.removeItem('ductape_token');
localStorage.removeItem('ductape_refresh');
showLoginPage();
}
}
// Call on app load
initializeAuth();
Refreshing Sessions
Manual Refresh
const refreshToken = localStorage.getItem('ductape_refresh');
const refreshed = await ductape.sessions.refresh({
tag: 'user-session',
refreshToken: refreshToken
});
// Update stored tokens
localStorage.setItem('ductape_token', refreshed.token);
localStorage.setItem('ductape_refresh', refreshed.refreshToken);
console.log('Session refreshed');
Auto-refresh with Interceptor
class SessionManager {
private token: string | null = null;
private refreshToken: string | null = null;
private refreshTimer: number | null = null;
private sessionTag: string = 'user-session';
async login(userData: { userId: string; email: string; role: string }) {
const session = await ductape.sessions.start({
tag: this.sessionTag,
data: userData,
expiresIn: 3600 // 1 hour
});
this.token = session.token;
this.refreshToken = session.refreshToken;
localStorage.setItem('ductape_token', session.token);
localStorage.setItem('ductape_refresh', session.refreshToken);
// Schedule refresh 5 minutes before expiry
this.scheduleRefresh(3600 - 300); // 55 minutes
}
private scheduleRefresh(delaySeconds: number) {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
this.refreshTimer = setTimeout(() => {
this.refreshSession();
}, delaySeconds * 1000);
}
private async refreshSession() {
try {
const refreshed = await ductape.sessions.refresh({
tag: this.sessionTag,
refreshToken: this.refreshToken!
});
this.token = refreshed.token;
this.refreshToken = refreshed.refreshToken;
localStorage.setItem('ductape_token', refreshed.token);
localStorage.setItem('ductape_refresh', refreshed.refreshToken);
// Schedule next refresh
this.scheduleRefresh(3600 - 300);
console.log('Session refreshed automatically');
} catch (error) {
console.error('Failed to refresh session:', error);
this.logout();
}
}
async logout() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
if (this.token) {
try {
const verified = await ductape.sessions.verify({
tag: this.sessionTag,
token: this.token
});
await ductape.sessions.revoke({
tag: this.sessionTag,
sessionId: verified.sessionId
});
} catch (error) {
console.error('Failed to revoke session:', error);
}
}
this.token = null;
this.refreshToken = null;
localStorage.removeItem('ductape_token');
localStorage.removeItem('ductape_refresh');
// Redirect to login
window.location.href = '/login';
}
}
// Usage
const sessionManager = new SessionManager();
await sessionManager.login({
userId: 'user-123',
email: 'user@example.com',
role: 'user'
});
Revoking Sessions
Revoke Current Session (Logout)
const token = localStorage.getItem('ductape_token');
// First verify to get session ID
const verified = await ductape.sessions.verify({
tag: 'user-session',
token
});
// Then revoke
await ductape.sessions.revoke({
tag: 'user-session',
sessionId: verified.sessionId
});
localStorage.removeItem('ductape_token');
localStorage.removeItem('ductape_refresh');
// Redirect to login
window.location.href = '/login';
Revoke All Sessions
await ductape.sessions.revokeAll({
tag: 'user-session',
identifier: 'user-123'
});
// User will be logged out from all devices
Revoke All Except Current
const token = localStorage.getItem('ductape_token');
const currentSession = await ductape.sessions.verify({
tag: 'user-session',
token
});
await ductape.sessions.revokeAll({
tag: 'user-session',
identifier: currentSession.data.userId,
excludeCurrentSession: currentSession.sessionId
});
alert('All other sessions have been revoked');
Listing Active Sessions
const result = await ductape.sessions.list({
tag: 'user-session',
identifier: 'user-123'
});
console.log(`User has ${result.count} active sessions`);
result.sessions.forEach(session => {
console.log(`- Session ${session.sessionId}`);
console.log(` Created: ${session.createdAt}`);
console.log(` Expires: ${session.expiresAt}`);
});
Complete Authentication Example
import { Ductape } from '@ductape/client';
class AuthService {
private ductape: Ductape;
private currentUser: any = null;
private sessionTag = 'user-session';
constructor(ductape: Ductape) {
this.ductape = ductape;
}
async login(email: string, password: string) {
try {
// 1. Verify credentials with your backend
const user = await this.verifyCredentials(email, password);
// 2. Create Ductape session with encrypted data
const session = await this.ductape.sessions.start({
tag: this.sessionTag,
data: {
userId: user.id,
email: user.email,
role: user.role,
name: user.name,
// Any other data you want encrypted
accountType: user.accountType
},
expiresIn: 86400 // 24 hours
});
// 3. Store tokens
localStorage.setItem('ductape_token', session.token);
localStorage.setItem('ductape_refresh', session.refreshToken);
this.currentUser = user;
// 4. Return user info
return user;
} catch (error) {
console.error('Login failed:', error);
throw error;
}
}
async logout() {
const token = localStorage.getItem('ductape_token');
if (token) {
try {
// Verify to get session ID
const verified = await this.ductape.sessions.verify({
tag: this.sessionTag,
token
});
// Revoke session
await this.ductape.sessions.revoke({
tag: this.sessionTag,
sessionId: verified.sessionId
});
} catch (error) {
console.error('Failed to revoke session:', error);
}
}
localStorage.removeItem('ductape_token');
localStorage.removeItem('ductape_refresh');
this.currentUser = null;
// Redirect to login
window.location.href = '/login';
}
async checkAuth(): Promise<boolean> {
const token = localStorage.getItem('ductape_token');
if (!token) {
return false;
}
try {
const result = await this.ductape.sessions.verify({
tag: this.sessionTag,
token
});
// Extract user data from decrypted session
this.currentUser = {
id: result.data.userId,
email: result.data.email,
role: result.data.role,
name: result.data.name
};
return true;
} catch (error) {
localStorage.removeItem('ductape_token');
localStorage.removeItem('ductape_refresh');
return false;
}
}
getCurrentUser() {
return this.currentUser;
}
isAuthenticated(): boolean {
return this.currentUser !== null;
}
hasRole(role: string): boolean {
return this.currentUser?.role === role;
}
private async verifyCredentials(email: string, password: string) {
// Implement your credential verification
// This could call your backend API
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!response.ok) {
throw new Error('Invalid credentials');
}
return await response.json();
}
}
// Usage
const ductape = new Ductape({
accessKey: 'your-access-key',
product: 'your-product',
env: 'prd'
});
const auth = new AuthService(ductape);
// Login
try {
const user = await auth.login('user@example.com', 'password123');
console.log('Logged in as:', user.name);
} catch (error) {
alert('Login failed');
}
// Check auth on page load
if (await auth.checkAuth()) {
console.log('User is authenticated');
} else {
window.location.href = '/login';
}
// Logout
await auth.logout();
Session Data Encryption
All data passed in the data field is automatically encrypted:
// Data is encrypted in the JWT
const session = await ductape.sessions.start({
tag: 'user-session',
data: {
userId: 'user-123',
email: 'user@example.com',
// Sensitive data is encrypted
creditCardLast4: '4242',
subscriptionTier: 'premium'
}
});
// Data is decrypted when verifying
const verified = await ductape.sessions.verify({
tag: 'user-session',
token: session.token
});
// Access decrypted data
console.log(verified.data.creditCardLast4); // "4242"
console.log(verified.data.subscriptionTier); // "premium"
Role-Based Access Control
class PermissionGuard {
private auth: AuthService;
constructor(auth: AuthService) {
this.auth = auth;
}
requireAuth() {
if (!this.auth.isAuthenticated()) {
window.location.href = '/login';
throw new Error('Not authenticated');
}
}
requireRole(role: string) {
this.requireAuth();
if (!this.auth.hasRole(role)) {
window.location.href = '/forbidden';
throw new Error('Insufficient permissions');
}
}
requireAnyRole(roles: string[]) {
this.requireAuth();
const user = this.auth.getCurrentUser();
if (!roles.includes(user.role)) {
window.location.href = '/forbidden';
throw new Error('Insufficient permissions');
}
}
}
// Usage
const guard = new PermissionGuard(auth);
// Protect a function
async function deleteUser(userId: string) {
guard.requireRole('admin');
await ductape.databases.delete({
table: 'users',
where: { id: userId }
});
}
// Protect a page
function loadAdminPage() {
guard.requireRole('admin');
// Load admin page content
}
Session Activity Dashboard
class SessionDashboard {
private ductape: Ductape;
private sessionTag = 'user-session';
constructor(ductape: Ductape) {
this.ductape = ductape;
}
async loadSessions(identifier: string) {
const result = await this.ductape.sessions.list({
tag: this.sessionTag,
identifier
});
this.renderSessions(result.sessions);
}
async revokeSession(sessionId: string) {
if (!confirm('Revoke this session?')) {
return;
}
await this.ductape.sessions.revoke({
tag: this.sessionTag,
sessionId
});
// Reload sessions
const token = localStorage.getItem('ductape_token');
const verified = await this.ductape.sessions.verify({
tag: this.sessionTag,
token
});
await this.loadSessions(verified.data.userId);
}
async revokeAllOthers(identifier: string) {
if (!confirm('Revoke all other sessions? This will log you out from all other devices.')) {
return;
}
const currentToken = localStorage.getItem('ductape_token');
const currentSession = await this.ductape.sessions.verify({
tag: this.sessionTag,
token: currentToken
});
await this.ductape.sessions.revokeAll({
tag: this.sessionTag,
identifier,
excludeCurrentSession: currentSession.sessionId
});
alert('All other sessions revoked');
await this.loadSessions(identifier);
}
private renderSessions(sessions: any[]) {
const container = document.getElementById('sessions-list');
container.innerHTML = '';
sessions.forEach(session => {
const item = document.createElement('div');
item.className = 'session-item';
const isCurrentSession = this.isCurrentSession(session);
item.innerHTML = `
<div class="session-info">
<strong>${isCurrentSession ? 'Current Session' : 'Session'}</strong>
<span>Created: ${new Date(session.createdAt).toLocaleString()}</span>
<span>Expires: ${new Date(session.expiresAt).toLocaleString()}</span>
</div>
${!isCurrentSession ? `
<button onclick="dashboard.revokeSession('${session.sessionId}')">
Revoke
</button>
` : ''}
`;
container.appendChild(item);
});
}
private isCurrentSession(session: any): boolean {
const currentToken = localStorage.getItem('ductape_token');
// You'd need to decode the JWT to check if it matches
return false; // Simplified for example
}
}
// Usage
const dashboard = new SessionDashboard(ductape);
await dashboard.loadSessions('user-123');
Best Practices
- Always use the
tagfield: Different session types should have different tags - Store tokens securely: Use httpOnly cookies in production when possible
- Encrypt sensitive data: All data in the
datafield is automatically encrypted - Implement auto-refresh: Refresh tokens before they expire
- Handle token expiry: Gracefully handle expired tokens
- Use HTTPS: Always use HTTPS in production
- Implement CSRF protection: Protect against cross-site request forgery
- Limit session duration: Use reasonable expiry times
- Revoke on logout: Always revoke sessions on logout
- Monitor active sessions: Allow users to view and revoke sessions
API Reference
Session Start Input
interface ISessionStartInput {
/** Session tag (required) */
tag: string;
/** Encrypted session data */
data: Record<string, unknown>;
/** User identifier for listing/revoking */
identifier?: string;
/** Session expiration in seconds */
expiresIn?: number;
/** Product tag (optional, uses default) */
product?: string;
/** Environment (optional, uses default) */
env?: string;
}
Session Verify Input
interface ISessionVerifyInput {
/** Session tag (required) */
tag: string;
/** Session token to verify */
token: string;
/** Product tag (optional, uses default) */
product?: string;
/** Environment (optional, uses default) */
env?: string;
}