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
- 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;
}