Skip to main content

Lifecycle Hooks

Three primary hooks control your app's integration with the marketplace session lifecycle. An optional fourth hook handles session extensions.

onSessionStart

Purpose: Provision user and initialize app state.

When called: After JWT validation, before timer starts.

Common tasks:

  • Create user account in your system
  • Generate app authentication token
  • Store session data
  • Initialize user preferences
  • Load user data

Example

onSessionStart: async (context: SessionStartContext) => {
console.log('[Session] Starting for user:', context.userId);

// 1. Check if user exists
let user = await getUser(context.userId);

// 2. Create if new user
if (!user) {
user = await createUser({
id: context.userId,
email: context.email,
source: 'marketplace',
createdAt: new Date(),
});
console.log('[Session] Created new user:', user.id);
}

// 3. Generate session token
const token = await generateSessionToken({
userId: user.id,
sessionId: context.sessionId,
expiresAt: context.expiresAt,
});

// 4. Store in app state
appState.setAuth({
token,
user,
expiresAt: context.expiresAt,
source: 'marketplace',
});

// 5. Load user data
await loadUserPreferences(user.id);
await loadUserContent(user.id);

console.log('[Session] User authenticated and initialized');
},

Error Handling

If onSessionStart throws an error, the session will not start:

onSessionStart: async (context: SessionStartContext) => {
try {
await authenticateUser(context);
} catch (error) {
console.error('[Session] Failed to start:', error);
showError('Failed to initialize session. Please try again.');
throw new Error('Session initialization failed');
}
},

onSessionWarning

Purpose: Alert user before session expires.

When called: At warning threshold (default: 5 minutes remaining).

Common tasks:

  • Show warning modal/notification
  • Offer session extension
  • Auto-save user work
  • Prompt to save changes

Example

onSessionWarning: async (context: SessionWarningContext) => {
console.log('[Session] Warning! Only', context.minutesRemaining, 'minutes left');

// 1. Auto-save user work
await autoSaveUserWork();

// 2. Show warning modal
showModal({
title: 'Session Expiring Soon',
message: `Your session will expire in ${context.minutesRemaining} minutes.`,
buttons: [
{
label: 'Extend Session',
action: async () => {
try {
await sdk.extendSession(30);
showNotification('Session extended!');
} catch (error) {
showError('Failed to extend session');
}
},
},
{
label: 'Save & Exit',
action: async () => {
await saveAllChanges();
sdk.endSession('manual');
},
},
{
label: 'Continue',
action: () => {
// Dismiss modal, continue working
},
},
],
});
},

Customizing Warning Threshold

const sdk = new MarketplaceSDK({
// ... other options
warningThresholdMinutes: 10, // Warn 10 minutes before expiration
});

onSessionEnd

Purpose: Clean up app state when session ends.

When called: When session expires or user manually ends.

Common tasks:

  • Clear authentication state
  • Log user out
  • Save analytics
  • Clear cached data
  • Redirect to marketplace (optional)

Example

onSessionEnd: async (context: SessionEndContext) => {
console.log('[Session] Ending. Reason:', context.reason);
console.log('[Session] Duration:', context.actualDurationMinutes, 'minutes');

// 1. Save analytics
await logSessionEnd({
userId: context.userId,
sessionId: context.sessionId,
duration: context.actualDurationMinutes,
reason: context.reason,
timestamp: Date.now(),
});

// 2. Clear authentication
localStorage.removeItem('auth_token');
localStorage.removeItem('user_data');
appState.clearAuth();

// 3. Clear any cached data
await clearUserCache();

// 4. Show farewell message
if (context.reason === 'manual') {
showNotification('Session ended. Thank you for using our app!');
} else if (context.reason === 'expired') {
showNotification('Session expired. Please return via marketplace to continue.');
}

console.log('[Session] Cleanup complete');
},

Preventing Redirect

By default, SDK redirects to marketplace after session ends. To prevent this:

const sdk = new MarketplaceSDK({
// ... other options
marketplaceUrl: undefined, // Disable redirect
});

// Or handle redirect manually in onSessionEnd:
onSessionEnd: async (context: SessionEndContext) => {
await cleanup();

if (context.reason === 'expired') {
window.location.href = 'https://platform.generalwisdom.com?session=expired';
} else {
window.location.href = 'https://platform.generalwisdom.com';
}
},

onSessionExtend

Purpose: Handle session extension requests.

When called: After successful session extension.

Common tasks:

  • Update stored expiration time
  • Refresh auth tokens
  • Show confirmation
  • Update UI timer

Example

onSessionExtend: async (context: SessionExtendContext) => {
console.log('[Session] Extended by', context.extensionMinutes, 'minutes');
console.log('[Session] New expiration:', new Date(context.newExpiresAt * 1000));

// 1. Update stored expiration
const auth = appState.getAuth();
auth.expiresAt = context.newExpiresAt;
appState.setAuth(auth);

// 2. Refresh app auth token if needed
if (needsTokenRefresh(auth)) {
const newToken = await refreshAppToken(auth.token);
auth.token = newToken;
appState.setAuth(auth);
}

// 3. Show confirmation
showNotification(`Session extended! You have ${context.extensionMinutes} more minutes.`);

// 4. Log analytics
await logSessionExtension({
userId: context.userId,
sessionId: context.sessionId,
extensionMinutes: context.extensionMinutes,
timestamp: Date.now(),
});
},

Context Types

interface SessionStartContext {
sessionId: string;
applicationId: string;
userId: string;
orgId?: string;
email?: string;
startTime: number; // Unix timestamp
expiresAt: number; // Unix timestamp
durationMinutes: number;
jwt: string; // Original JWT token
}

interface SessionEndContext {
sessionId: string;
userId: string;
reason: 'expired' | 'manual' | 'error';
actualDurationMinutes: number;
}

interface SessionWarningContext {
sessionId: string;
userId: string;
minutesRemaining: number;
expiresAt: number;
}

interface SessionExtendContext {
sessionId: string;
userId: string;
extensionMinutes: number;
newExpiresAt: number;
previousExpiresAt: number;
}