Advanced Features
Session Management
Checking Session Status
// Check if session is active
if (sdk.hasActiveSession()) {
console.log('Session is active');
}
// Get current session data
const session = sdk.getSession();
if (session) {
console.log('User ID:', session.userId);
console.log('Expires at:', new Date(session.expiresAt * 1000));
console.log('Time remaining:', session.getRemainingSeconds(), 'seconds');
}
Manually Ending a Session
// End session early
await sdk.endSession('manual');
// With reason
await sdk.endSession('user_requested');
Extending a Session
Request more time (requires backend support):
try {
await sdk.extendSession(30); // Request 30 more minutes
console.log('Session extended successfully');
} catch (error) {
console.error('Failed to extend:', error);
showError('Could not extend session. Please try again.');
}
Pausing/Resuming Timer (UI Only)
// Pause timer (visual only, session still expires)
sdk.pauseTimer();
// Resume timer
sdk.resumeTimer();
// Check if paused
if (sdk.isTimerPaused()) {
console.log('Timer is paused');
}
note
Pausing only affects the UI display. The actual session expiration continues server-side.
Multi-Tab Coordination
The SDK automatically coordinates sessions across browser tabs:
- Master tab election — first tab becomes master
- Automatic timer synchronization — all tabs stay in sync
- Shared session state via BroadcastChannel API
- Seamless master failover if the active tab closes
No configuration needed — works out of the box.
Listening for multi-tab events:
window.addEventListener('marketplace-session-sync', (event) => {
console.log('[MultiTab] Session synced:', event.detail);
});
Fallback: Modern browsers use BroadcastChannel. Older browsers fall back to localStorage events.
Heartbeat System
Periodic server communication for time synchronization:
const sdk = new MarketplaceSDK({
// ... other options
heartbeat: {
enabled: true,
intervalSeconds: 30,
endpoint: 'https://api.example.com/sessions/heartbeat',
},
});
Backend Heartbeat Handler
@app.route('/sessions/heartbeat', methods=['POST'])
def session_heartbeat():
data = request.json
session_id = data['sessionId']
client_time = data['timestamp']
# Validate session still active
session = get_session(session_id)
if not session or session.is_expired():
return jsonify({'error': 'Session expired'}), 410
# Return server time for sync
server_time = int(time.time())
update_session_heartbeat(session_id, server_time)
return jsonify({
'serverTime': server_time,
'expiresAt': session.expires_at,
'remainingSeconds': session.get_remaining_seconds()
})
Failure Handling
- 3 heartbeat failures triggers automatic stop (by design — prevents spam)
- Session continues without heartbeat after failure
- Heartbeat does not resume automatically after network recovery
Visibility API Integration
Auto-pause when tab is hidden (battery-friendly):
const sdk = new MarketplaceSDK({
// ... other options
pauseWhenHidden: true,
});
The SDK automatically:
- Pauses the timer when the tab is hidden
- Resumes when the tab becomes visible again
- Syncs time with server on resume
Backend JWT Validation
Alternative to JWKS for sensitive applications:
const sdk = new MarketplaceSDK({
// ... other options
validateJWTBackend: true,
validationEndpoint: 'https://api.example.com/validate-jwt',
});
Backend Validation Endpoint
@app.route('/validate-jwt', methods=['POST'])
def validate_jwt():
jwt_token = request.json['jwt']
try:
response = requests.post(
'https://api.generalwisdom.com/validate-session',
headers={'Authorization': f'Bearer {jwt_token}'}
)
if response.status_code == 200:
claims = response.json()
return jsonify({'valid': True, 'claims': claims})
else:
return jsonify({'valid': False, 'error': 'Invalid JWT'}), 401
except Exception as e:
return jsonify({'valid': False, 'error': str(e)}), 500
Security Best Practices
JWT Validation
Do:
- Always validate JWT signature (SDK does this automatically)
- Check expiration time
- Verify issuer matches marketplace
- Validate applicationId matches your app
- Use JWKS for signature verification (production)
Do not:
- Skip JWT validation
- Trust client-side validation only
- Store JWT in localStorage without encryption
- Share JWT across origins
Token Storage
Do:
- Use httpOnly cookies for sensitive tokens (if possible)
- Use chrome.storage for extensions (browser-encrypted)
- Clear tokens immediately on session end
- Set appropriate expiration times
Do not:
- Store sensitive data in localStorage without encryption
- Keep tokens after session ends
- Share tokens between users/sessions
HTTPS Requirements
Production applications must have:
- HTTPS for all API endpoints
- Valid SSL certificate
- Secure WebSocket connections (wss://)
- HSTS headers enabled
Rate Limiting
Protect your backend endpoints:
from flask_limiter import Limiter
limiter = Limiter(app, key_func=lambda: request.headers.get('X-Session-ID'))
@app.route('/auth/marketplace', methods=['POST'])
@limiter.limit("10 per minute")
def marketplace_auth():
# ... auth logic
Secrets Management
Do:
- Use environment variables for secrets
- Use AWS Secrets Manager / Azure Key Vault
- Rotate secrets regularly
- Never commit secrets to git
Do not:
- Hard-code API keys in source code
- Commit
.envfiles - Share secrets in plain text
Input Validation
onSessionStart: async (context: SessionStartContext) => {
if (context.email && !isValidEmail(context.email)) {
throw new Error('Invalid email format');
}
if (!isValidUUID(context.userId)) {
throw new Error('Invalid user ID format');
}
const sanitizedEmail = sanitize(context.email);
// ... rest of logic
},
Deployment Checklist
- Replace test JWKS URL with production
- Update
applicationIdto registered value - Enable HTTPS for all endpoints
- Configure proper CORS headers
- Set up secrets management
- Enable rate limiting on auth endpoints
- Configure logging and monitoring
- Test JWT validation with production keys
- Test session extension flow
- Test multi-tab coordination
- Set proper CSP headers
- Enable HSTS
- Configure marketplace redirect URL
- Test error handling and fallbacks
- Load test auth endpoints
- Set up alerts for auth failures
Monitoring
Track key metrics:
onSessionStart: async (context) => {
analytics.track('marketplace_session_start', {
userId: context.userId,
sessionId: context.sessionId,
durationMinutes: context.durationMinutes,
});
},
onSessionEnd: async (context) => {
analytics.track('marketplace_session_end', {
userId: context.userId,
sessionId: context.sessionId,
reason: context.reason,
actualDuration: context.actualDurationMinutes,
});
},
Monitor errors:
onSessionStart: async (context) => {
try {
await authenticateUser(context);
} catch (error) {
errorTracker.captureException(error, {
extra: {
userId: context.userId,
sessionId: context.sessionId,
},
});
throw error;
}
},