import type Keycloak from 'keycloak-js' export const useKeycloak = () => { const config = useRuntimeConfig() const keycloak = ref(null) const isAuthenticated = ref(false) const user = ref(null) const token = ref(null) const isInitialized = ref(false) // Get the correct base URL considering proxy setup const getBaseUrl = () => { if (process.server) { // In production, always use the configured HTTPS base URL return config.public.baseUrl } // Client-side: detect if we're behind a proxy const currentOrigin = window.location.origin const isProduction = !currentOrigin.includes('localhost') && !currentOrigin.includes('127.0.0.1') if (isProduction) { // Force HTTPS in production environments return config.public.baseUrl } // Development: use current origin return currentOrigin } const logDebug = (message: string, data?: any) => { if (config.public.keycloakDebug) { console.log(`[KEYCLOAK] ${message}`, data) } } const initKeycloak = async () => { if (process.server) return false try { const baseUrl = getBaseUrl() logDebug('Initializing Keycloak', { baseUrl, keycloakUrl: config.public.keycloak.url, realm: config.public.keycloak.realm, clientId: config.public.keycloak.clientId }) // Dynamically import keycloak-js const KeycloakModule = await import('keycloak-js') const KeycloakConstructor = KeycloakModule.default keycloak.value = new KeycloakConstructor({ url: config.public.keycloak.url, realm: config.public.keycloak.realm, clientId: config.public.keycloak.clientId, }) const authenticated = await keycloak.value.init({ onLoad: 'check-sso', // Use proper HTTPS redirect URI redirectUri: `${baseUrl}/auth/callback`, // Disable all iframe-based features that cause CORS issues checkLoginIframe: false, silentCheckSsoRedirectUri: undefined, // Disable silent SSO check enableLogging: false, // Reduce console noise // Use standard flow compatible with proxy setups flow: 'standard', responseMode: 'query', // Use query params instead of fragments // Disable third-party cookie checks checkLoginIframeInterval: 0, // PKCE for security pkceMethod: 'S256', // Timeout settings messageReceiveTimeout: 10000, // Disable adapter features that can cause issues in proxied environments adapter: 'default' }) logDebug('Keycloak initialization result', { authenticated, redirectUri: `${baseUrl}/auth/callback`, checkLoginIframe: false, silentSso: 'disabled' }) isAuthenticated.value = authenticated isInitialized.value = true if (authenticated && keycloak.value.token) { token.value = keycloak.value.token || null user.value = { id: keycloak.value.subject, username: keycloak.value.tokenParsed?.preferred_username, email: keycloak.value.tokenParsed?.email, firstName: keycloak.value.tokenParsed?.given_name, lastName: keycloak.value.tokenParsed?.family_name, fullName: keycloak.value.tokenParsed?.name, roles: keycloak.value.tokenParsed?.realm_access?.roles || [], } // Set up token refresh keycloak.value.onTokenExpired = () => { keycloak.value?.updateToken(30).then((refreshed) => { if (refreshed) { token.value = keycloak.value?.token || null console.log('Token refreshed') } else { console.log('Token still valid') } }).catch(() => { console.log('Failed to refresh token') logout() }) } } return authenticated } catch (error) { console.error('Failed to initialize Keycloak:', error) isInitialized.value = true return false } } const login = async () => { if (!keycloak.value) { await initKeycloak() } if (keycloak.value) { try { const baseUrl = getBaseUrl() logDebug('Starting login', { redirectUri: `${baseUrl}/dashboard` }) await keycloak.value.login({ redirectUri: `${baseUrl}/dashboard` }) } catch (error) { console.error('Login failed:', error) logDebug('Login error', error) throw error } } } const logout = async () => { if (keycloak.value) { try { await keycloak.value.logout({ redirectUri: window.location.origin }) } catch (error) { console.error('Logout failed:', error) } } // Clear local state isAuthenticated.value = false user.value = null token.value = null } const getToken = () => { return token.value } const hasRole = (role: string) => { return user.value?.roles?.includes(role) || false } const hasAnyRole = (roles: string[]) => { return roles.some(role => hasRole(role)) } return { keycloak: readonly(keycloak), isAuthenticated: readonly(isAuthenticated), user: readonly(user), token: readonly(token), isInitialized: readonly(isInitialized), initKeycloak, login, logout, getToken, hasRole, hasAnyRole, } }