Skip to main content

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 directly from the frontend.

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

  1. Always use the tag field: Different session types should have different tags
  2. Store tokens securely: Use httpOnly cookies in production when possible
  3. Encrypt sensitive data: All data in the data field is automatically encrypted
  4. Implement auto-refresh: Refresh tokens before they expire
  5. Handle token expiry: Gracefully handle expired tokens
  6. Use HTTPS: Always use HTTPS in production
  7. Implement CSRF protection: Protect against cross-site request forgery
  8. Limit session duration: Use reasonable expiry times
  9. Revoke on logout: Always revoke sessions on logout
  10. 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;
}

Next Steps