// Keycloak (OIDC) integration. The app is anonymous by default; logging in as a // user with the realm role `manager` unlocks the full data view. Everything here // degrades gracefully: if Keycloak is unreachable, initAuth() resolves anyway and // the app stays in the anonymous tier. import Keycloak from 'keycloak-js'; const keycloak = new Keycloak({ url: 'https://keycloak.intense-sale.ru', realm: 'intense-sale', clientId: 'mapdash', }); let manager = false; function computeManager(): boolean { const roles = (keycloak.tokenParsed as { realm_access?: { roles?: string[] } } | undefined) ?.realm_access?.roles ?? []; return keycloak.authenticated === true && roles.includes('manager'); } /** * Initialise Keycloak with a silent SSO check (no forced login). Never rejects — * on any failure we log and continue as an anonymous visitor. */ export async function initAuth(): Promise { try { await keycloak.init({ onLoad: 'check-sso', silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', pkceMethod: 'S256', }); manager = computeManager(); } catch (e) { console.warn('Keycloak init failed — continuing as anonymous', e); manager = false; } } export function isManager(): boolean { return manager; } export function isAuthenticated(): boolean { return keycloak.authenticated === true; } export function login(): void { // Return to the current view (filters in the URL) after login. keycloak.login({ redirectUri: window.location.href }); } export function logout(): void { keycloak.logout({ redirectUri: window.location.origin }); } /** * Authorization header for API calls. Refreshes the token when it is close to * expiry. Returns an empty object for anonymous callers (backend then serves the * truncated tier). */ export async function authHeader(): Promise> { if (!keycloak.authenticated) return {}; try { await keycloak.updateToken(30); } catch { /* refresh failed — send the current token; backend falls back to anon if invalid */ } return keycloak.token ? { Authorization: `Bearer ${keycloak.token}` } : {}; }