Session Termination
Sessions end when their expiration time is reached, when the user manually ends the session, or when an error occurs. Your application must handle termination gracefully by cleaning up state, saving user work, and optionally redirecting back to the marketplace.
Termination Flow
sequenceDiagram
participant Timer as Session Timer
participant SDK as Provider SDK
participant App as Your Application
participant User
participant Marketplace as GW Marketplace
alt Session Expired
Timer->>SDK: Expiration reached
else User Ends Manually
User->>App: Clicks "End Session"
App->>SDK: sdk.endSession('manual')
else Error Occurs
SDK->>SDK: Unrecoverable error detected
end
SDK->>App: onSessionEnd(context) fires
App->>App: Save user work
App->>App: Clear authentication state
App->>App: Clear cached data
App->>App: Log analytics
App-->>User: Show farewell message
SDK->>Marketplace: Redirect (if configured)
Termination Reasons
The onSessionEnd hook receives a reason field indicating why the session ended:
| Reason | Description | Typical Handling |
|---|---|---|
expired | Session time elapsed naturally | Show "session expired" message, redirect |
manual | User or app ended the session explicitly | Show "goodbye" message, redirect |
error | An unrecoverable error occurred | Show error message, offer retry |
Handling Termination with the SDK
The onSessionEnd Hook
This is the primary hook for session cleanup. It fires regardless of how the session ended:
const sdk = new MarketplaceSDK({
applicationId: 'your-app-id',
apiKey: 'gwsk_...',
environment: 'production',
hooks: {
onSessionEnd: async (context) => {
console.log('[Session] Ending. Reason:', context.reason);
console.log('[Session] Duration:', context.actualDurationMinutes, 'min');
// 1. Save any unsaved user work
await autoSaveUserWork();
// 2. Log session analytics
await logSessionEnd({
userId: context.userId,
sessionId: context.sessionId,
duration: context.actualDurationMinutes,
reason: context.reason,
timestamp: Date.now(),
});
// 3. Clear authentication state
localStorage.removeItem('auth_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_data');
// 4. Clear cached application data
await clearUserCache();
// 5. Reset application UI state
appState.reset();
console.log('[Session] Cleanup complete');
},
},
});
Context Object
interface SessionEndContext {
sessionId: string;
userId: string;
reason: 'expired' | 'manual' | 'error';
actualDurationMinutes: number;
}
Manually Ending a Session
Your application can programmatically end a session at any time:
// End with a reason
await sdk.endSession('manual');
// Common scenarios for manual termination:
// - User clicks a "Leave Session" button
// - User completes their workflow early
// - Application detects inactivity
Adding an "End Session" Button
function EndSessionButton({ sdk }) {
const handleEnd = async () => {
const confirmed = window.confirm(
'Are you sure you want to end your session?'
);
if (confirmed) {
await sdk.endSession('manual');
}
};
return <button onClick={handleEnd}>End Session</button>;
}
Redirect Behavior
By default, the SDK redirects users to the marketplace after session termination.
Configuring the Redirect URL
const sdk = new MarketplaceSDK({
applicationId: 'your-app-id',
apiKey: 'gwsk_...',
marketplaceUrl: 'https://platform.generalwisdom.com',
// Users are redirected here after session ends
});
Disabling Automatic Redirect
If you want to handle navigation yourself:
const sdk = new MarketplaceSDK({
applicationId: 'your-app-id',
apiKey: 'gwsk_...',
marketplaceUrl: undefined, // Disable automatic redirect
hooks: {
onSessionEnd: async (context) => {
await cleanup();
// Custom redirect logic
if (context.reason === 'expired') {
window.location.href =
'https://platform.generalwisdom.com?session=expired';
} else {
window.location.href = 'https://platform.generalwisdom.com';
}
},
},
});
Adding a Delay Before Redirect
Show a farewell message before redirecting:
onSessionEnd: async (context) => {
await cleanup();
// Show farewell UI
showFarewellScreen(context.reason);
// Redirect after delay
setTimeout(() => {
window.location.href = 'https://platform.generalwisdom.com';
}, 3000); // 3-second delay
},
Cleanup Checklist
When a session ends, ensure your application:
| Task | Why | Priority |
|---|---|---|
| Save unsaved user work | Prevent data loss | Critical |
| Clear auth tokens | Prevent unauthorized access | Critical |
| Log session analytics | Track usage and behavior | High |
| Clear user cache | Free memory, ensure privacy | High |
| Close open connections | Prevent resource leaks | Medium |
| Reset UI to unauthenticated state | Prepare for next session | Medium |
| Cancel pending network requests | Avoid errors after teardown | Low |
Error Handling During Cleanup
Cleanup should be resilient. Never let a single failure prevent the rest of the cleanup:
onSessionEnd: async (context) => {
const errors: Error[] = [];
// Each step is independent - continue even if one fails
try {
await autoSaveUserWork();
} catch (err) {
errors.push(err as Error);
console.error('[Cleanup] Failed to save work:', err);
}
try {
await logSessionAnalytics(context);
} catch (err) {
errors.push(err as Error);
console.error('[Cleanup] Failed to log analytics:', err);
}
// These operations are synchronous and should not fail
localStorage.removeItem('auth_token');
localStorage.removeItem('refresh_token');
sessionStorage.clear();
try {
await clearUserCache();
} catch (err) {
errors.push(err as Error);
console.error('[Cleanup] Failed to clear cache:', err);
}
if (errors.length > 0) {
console.warn(`[Cleanup] Completed with ${errors.length} errors`);
}
},
Handling Termination Without the SDK
If you are managing the session manually (without the Provider SDK), set up a timer that monitors the JWT expiration:
function startSessionMonitor(expiresAt: number) {
const checkInterval = setInterval(() => {
const now = Math.floor(Date.now() / 1000);
const remaining = expiresAt - now;
if (remaining <= 0) {
clearInterval(checkInterval);
handleSessionExpired();
} else if (remaining <= 300) {
// 5 minutes warning
showExpirationWarning(remaining);
}
}, 1000);
return () => clearInterval(checkInterval);
}
async function handleSessionExpired() {
// Perform cleanup
await autoSaveUserWork();
localStorage.removeItem('auth_token');
sessionStorage.clear();
// Show message and redirect
showNotification('Your session has expired.');
setTimeout(() => {
window.location.href = 'https://platform.generalwisdom.com';
}, 2000);
}
Session End UX Patterns
Expired Session
function showExpiredScreen() {
renderOverlay({
title: 'Session Expired',
message: 'Your session time has ended. Visit the marketplace to start a new session.',
actions: [
{ label: 'Return to Marketplace', href: 'https://platform.generalwisdom.com' },
{ label: 'Purchase New Session', href: 'https://platform.generalwisdom.com/apps/your-app' },
],
});
}
Manual End
function showGoodbyeScreen() {
renderOverlay({
title: 'Session Ended',
message: 'Thank you for using our application. Your work has been saved.',
actions: [
{ label: 'Return to Marketplace', href: 'https://platform.generalwisdom.com' },
],
});
}
Next Steps
- Set up webhooks to receive server-side notifications when sessions end
- Review session creation to understand the full lifecycle
- Handle session extensions to let users extend before expiration