Session Management
Master session management, token handling, and authentication persistence in your Qlik applications.
Session Management Overview
Effective session management ensures your users have a smooth, secure experience while maintaining proper authentication state across their interactions with Qlik applications.
Token Lifecycle
Automatic token refresh and expiration handling
Security
Secure storage and transmission of authentication data
Auto-Recovery
Automatic session restoration and error recovery
Session Lifecycle Management
Basic Session Operations
Fundamental session management operations
typescript
import Qlik from 'qlik';
class BasicSessionManager {
private qlik: Qlik;
constructor(config: QlikConfig) {
this.qlik = new Qlik(config);
}
// Check current authentication status
async checkSession(): Promise<boolean> {
try {
const isAuthenticated = await this.qlik.isAuthenticated();
console.log('Session status:', isAuthenticated ? 'Active' : 'Inactive');
return isAuthenticated;
} catch (error) {
console.error('Session check failed:', error);
return false;
}
}
// Initialize new session
async createSession(): Promise<void> {
try {
await this.qlik.authenticateToQlik();
console.log('✅ New session created');
// Optional: Set up session monitoring
this.startSessionMonitoring();
} catch (error) {
console.error('❌ Session creation failed:', error);
throw error;
}
}
// End current session
async endSession(): Promise<void> {
try {
await this.qlik.logout();
console.log('✅ Session ended');
} catch (error) {
console.error('Session logout failed:', error);
}
}
// Get session information
async getSessionInfo(): Promise<any> {
try {
const sessionInfo = await this.qlik.getSessionInfo();
return {
userId: sessionInfo.userId,
tenantId: sessionInfo.tenantId,
expiry: sessionInfo.expiry,
lastActivity: new Date().toISOString()
};
} catch (error) {
console.error('Failed to get session info:', error);
return null;
}
}
private startSessionMonitoring(): void {
// Monitor session every 5 minutes
setInterval(async () => {
const isActive = await this.checkSession();
if (!isActive) {
console.warn('Session expired, attempting refresh...');
await this.handleSessionExpired();
}
}, 5 * 60 * 1000);
}
private async handleSessionExpired(): Promise<void> {
try {
await this.createSession();
} catch (error) {
console.error('Session refresh failed:', error);
// Emit session expired event for UI handling
this.emitSessionExpired();
}
}
private emitSessionExpired(): void {
window.dispatchEvent(new CustomEvent('qlik:session-expired'));
}
}
// Usage
const sessionManager = new BasicSessionManager({
host: 'your-tenant.us.qlikcloud.com',
webIntegrationId: 'your-web-integration-id'
});
// Initialize session
await sessionManager.createSession();
// Check session status
const isActive = await sessionManager.checkSession();
// Get session details
const sessionInfo = await sessionManager.getSessionInfo();
React Session Management
useSession Hook
React hook for session management with state tracking
typescript
// hooks/useSession.ts
import { useState, useEffect, useCallback, useRef } from 'react';
import Qlik from 'qlik';
interface SessionState {
isAuthenticated: boolean;
isLoading: boolean;
user: any | null;
error: string | null;
tokenExpiry: Date | null;
}
interface UseSessionReturn extends SessionState {
login: () => Promise<void>;
logout: () => Promise<void>;
refresh: () => Promise<void>;
clearError: () => void;
}
export function useSession(qlik: Qlik): UseSessionReturn {
const [state, setState] = useState<SessionState>({
isAuthenticated: false,
isLoading: true,
user: null,
error: null,
tokenExpiry: null
});
const refreshTimerRef = useRef<NodeJS.Timeout | null>(null);
// Initialize session on mount
useEffect(() => {
initializeSession();
// Cleanup on unmount
return () => {
if (refreshTimerRef.current) {
clearTimeout(refreshTimerRef.current);
}
};
}, []);
const initializeSession = async () => {
try {
setState(prev => ({ ...prev, isLoading: true, error: null }));
const isAuthenticated = await qlik.isAuthenticated();
if (isAuthenticated) {
const userInfo = await qlik.getUserInfo();
const tokenInfo = await qlik.getTokenInfo();
setState(prev => ({
...prev,
isAuthenticated: true,
user: userInfo,
tokenExpiry: new Date(tokenInfo.expiry),
isLoading: false
}));
scheduleTokenRefresh(new Date(tokenInfo.expiry));
} else {
setState(prev => ({
...prev,
isAuthenticated: false,
user: null,
tokenExpiry: null,
isLoading: false
}));
}
} catch (error) {
setState(prev => ({
...prev,
error: error instanceof Error ? error.message : 'Session initialization failed',
isLoading: false
}));
}
};
const login = useCallback(async () => {
try {
setState(prev => ({ ...prev, isLoading: true, error: null }));
await qlik.authenticateToQlik();
const userInfo = await qlik.getUserInfo();
const tokenInfo = await qlik.getTokenInfo();
setState(prev => ({
...prev,
isAuthenticated: true,
user: userInfo,
tokenExpiry: new Date(tokenInfo.expiry),
isLoading: false
}));
scheduleTokenRefresh(new Date(tokenInfo.expiry));
} catch (error) {
setState(prev => ({
...prev,
error: error instanceof Error ? error.message : 'Login failed',
isAuthenticated: false,
isLoading: false
}));
}
}, [qlik]);
const logout = useCallback(async () => {
try {
await qlik.logout();
if (refreshTimerRef.current) {
clearTimeout(refreshTimerRef.current);
refreshTimerRef.current = null;
}
setState({
isAuthenticated: false,
isLoading: false,
user: null,
error: null,
tokenExpiry: null
});
} catch (error) {
console.error('Logout failed:', error);
}
}, [qlik]);
const refresh = useCallback(async () => {
await initializeSession();
}, []);
const clearError = useCallback(() => {
setState(prev => ({ ...prev, error: null }));
}, []);
const scheduleTokenRefresh = (expiry: Date) => {
const refreshTime = expiry.getTime() - Date.now() - (5 * 60 * 1000); // 5 min buffer
if (refreshTime > 0) {
refreshTimerRef.current = setTimeout(async () => {
try {
await qlik.refreshToken();
await initializeSession();
} catch (error) {
console.error('Token refresh failed:', error);
await logout();
}
}, refreshTime);
}
};
return {
...state,
login,
logout,
refresh,
clearError
};
}
// Component usage
function SessionProvider({ children }: { children: React.ReactNode }) {
const qlik = new Qlik({
host: 'your-tenant.us.qlikcloud.com',
webIntegrationId: 'your-web-integration-id'
});
const session = useSession(qlik);
if (session.isLoading) {
return <div>Loading session...</div>;
}
if (session.error) {
return (
<div>
<p>Session error: {session.error}</p>
<button onClick={session.clearError}>Retry</button>
</div>
);
}
if (!session.isAuthenticated) {
return (
<div>
<button onClick={session.login}>Sign In</button>
</div>
);
}
return (
<div>
<div className="session-info">
<span>Welcome, {session.user?.name}</span>
<span>Token expires: {session.tokenExpiry?.toLocaleTimeString()}</span>
<button onClick={session.logout}>Sign Out</button>
</div>
{children}
</div>
);
}
Session Security
Secure Token Storage
Best practices for storing and managing authentication tokens
typescript
class SecureTokenStorage {
private readonly TOKEN_KEY = 'qlik_auth_token';
private readonly EXPIRY_KEY = 'qlik_token_expiry';
private readonly MAX_AGE = 24 * 60 * 60 * 1000; // 24 hours
// Store token securely
storeToken(token: string, expiry: string): boolean {
try {
// Validate token before storing
if (!this.validateToken(token)) {
throw new Error('Invalid token format');
}
const expiryTime = new Date(expiry).getTime();
const maxExpiryTime = Date.now() + this.MAX_AGE;
// Ensure expiry is not too far in the future
if (expiryTime > maxExpiryTime) {
throw new Error('Token expiry exceeds maximum allowed time');
}
// Store with additional metadata
const tokenData = {
token,
expiry,
stored: Date.now(),
origin: window.location.origin
};
localStorage.setItem(this.TOKEN_KEY, JSON.stringify(tokenData));
return true;
} catch (error) {
console.error('Token storage failed:', error);
return false;
}
}
// Retrieve token with validation
getToken(): { token: string; expiry: string } | null {
try {
const stored = localStorage.getItem(this.TOKEN_KEY);
if (!stored) {
return null;
}
const tokenData = JSON.parse(stored);
// Validate stored data structure
if (!tokenData.token || !tokenData.expiry || !tokenData.origin) {
this.clearToken();
return null;
}
// Verify origin matches (security check)
if (tokenData.origin !== window.location.origin) {
this.clearToken();
return null;
}
// Check if token is expired
const expiryTime = new Date(tokenData.expiry).getTime();
if (expiryTime <= Date.now()) {
this.clearToken();
return null;
}
return {
token: tokenData.token,
expiry: tokenData.expiry
};
} catch (error) {
console.error('Token retrieval failed:', error);
this.clearToken();
return null;
}
}
// Clear stored tokens
clearToken(): void {
try {
localStorage.removeItem(this.TOKEN_KEY);
} catch (error) {
console.error('Token clearing failed:', error);
}
}
// Validate token format
private validateToken(token: string): boolean {
// Basic JWT validation
if (!token || typeof token !== 'string') {
return false;
}
const parts = token.split('.');
if (parts.length !== 3) {
return false; // Not a valid JWT structure
}
try {
// Try to decode header and payload
atob(parts[0]);
atob(parts[1]);
return true;
} catch {
return false;
}
}
// Check if storage is available
isStorageAvailable(): boolean {
try {
const testKey = '__storage_test__';
localStorage.setItem(testKey, 'test');
localStorage.removeItem(testKey);
return true;
} catch {
return false;
}
}
}
// Usage with session manager
class SecureSessionManager {
private storage = new SecureTokenStorage();
private qlik: Qlik;
constructor(config: QlikConfig) {
this.qlik = new Qlik(config);
}
async initializeSecureSession(): Promise<void> {
if (!this.storage.isStorageAvailable()) {
console.warn('Secure storage not available, using memory-only session');
}
// Try to restore from secure storage
const storedTokens = this.storage.getToken();
if (storedTokens) {
try {
this.qlik.setAccessToken(storedTokens.token);
// Validate the restored token
const isValid = await this.qlik.isAuthenticated();
if (isValid) {
console.log('✅ Secure session restored');
return;
}
} catch (error) {
console.warn('Stored token validation failed:', error);
}
}
// Clear invalid tokens and start fresh
this.storage.clearToken();
await this.qlik.authenticateToQlik();
// Store new tokens securely
const token = await this.qlik.getAccessToken();
const tokenInfo = await this.qlik.getTokenInfo();
this.storage.storeToken(token, tokenInfo.expiry);
console.log('✅ New secure session established');
}
}
⚡ Session Management Best Practices
Automatic Refresh: Implement proactive token refresh before expiration
Secure Storage: Use secure storage methods and validate stored tokens
Error Recovery: Handle network issues and authentication failures gracefully
User Experience: Provide clear feedback on session status and actions
Memory Management: Clean up timers and event listeners on component unmount
Multi-tab Support: Coordinate sessions across multiple browser tabs
Offline Handling: Gracefully handle offline scenarios and reconnection
Security Validation: Always validate tokens and session data integrity
On this page
Overview
Getting Started
Examples