UNPKG

ssr-keycloak

Version:

SSR compatible Keycloak authentication library for React applications

283 lines (282 loc) 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KeycloakProvider = KeycloakProvider; exports.useKeycloak = useKeycloak; exports.useKeycloakSession = useKeycloakSession; exports.useKeycloakUser = useKeycloakUser; exports.useKeycloakAuth = useKeycloakAuth; exports.useKeycloakRoles = useKeycloakRoles; const jsx_runtime_1 = require("react/jsx-runtime"); // SSR Keycloak - React context ve hooks const react_1 = require("react"); const KeycloakContext = (0, react_1.createContext)(undefined); /** * Keycloak Provider - Client-side */ function KeycloakProvider({ config, children, onAuthSuccess, onAuthError, onAuthLogout, autoRefreshToken = true, refreshTokenInterval = 60000, enableLogging = false }) { const [isAuthenticated, setIsAuthenticated] = (0, react_1.useState)(false); const [isLoading, setIsLoading] = (0, react_1.useState)(true); const [error, setError] = (0, react_1.useState)(null); const [user, setUser] = (0, react_1.useState)(null); const [tokens, setTokens] = (0, react_1.useState)(null); const [session, setSession] = (0, react_1.useState)(null); // Server-side session'ı client'a aktar (0, react_1.useEffect)(() => { const initializeFromServer = async () => { try { setIsLoading(true); setError(null); // Server-side session'ı al const response = await fetch('/api/auth/session', { credentials: 'include' }); if (response.ok) { const sessionData = await response.json(); if (sessionData.isAuthenticated) { setSession(sessionData.session); setUser(sessionData.user); setTokens(sessionData.tokens); setIsAuthenticated(true); } } } catch (error) { console.error('Session initialization failed:', error); setError(error instanceof Error ? error.message : 'Session initialization failed'); } finally { setIsLoading(false); } }; initializeFromServer(); }, []); // Auto refresh token (0, react_1.useEffect)(() => { if (!autoRefreshToken || !isAuthenticated || !tokens) return; const interval = setInterval(async () => { try { const response = await fetch('/api/auth/refresh', { method: 'POST', credentials: 'include' }); if (response.ok) { const newTokens = await response.json(); setTokens(newTokens); if (session) { setSession({ ...session, tokens: newTokens, lastActivity: Date.now() }); } } else { // Refresh failed, logout await logout(); } } catch (error) { console.error('Auto refresh failed:', error); await logout(); } }, refreshTokenInterval); return () => clearInterval(interval); }, [autoRefreshToken, isAuthenticated, tokens, session, refreshTokenInterval]); // Login fonksiyonu const login = (0, react_1.useCallback)(async (options = {}) => { try { setError(null); const params = new URLSearchParams(); if (options.flow) params.append('flow', options.flow); if (options.redirectUri) params.append('redirectUri', options.redirectUri); if (options.scope) params.append('scope', options.scope); const response = await fetch(`/api/auth/login?${params.toString()}`, { method: 'POST', credentials: 'include' }); if (response.ok) { const { authUrl } = await response.json(); window.location.href = authUrl; } else { const errorData = await response.json(); throw new Error(errorData.error || 'Login failed'); } } catch (error) { console.error('Login failed:', error); setError(error instanceof Error ? error.message : 'Login failed'); onAuthError?.(error); } }, [onAuthError]); // Logout fonksiyonu const logout = (0, react_1.useCallback)(async (redirectUri) => { try { setError(null); const params = new URLSearchParams(); if (redirectUri) params.append('redirectUri', redirectUri); const response = await fetch(`/api/auth/logout?${params.toString()}`, { method: 'POST', credentials: 'include' }); if (response.ok) { const { logoutUrl } = await response.json(); window.location.href = logoutUrl; } else { // Fallback: local logout setIsAuthenticated(false); setUser(null); setTokens(null); setSession(null); onAuthLogout?.(); window.location.href = redirectUri || '/'; } } catch (error) { console.error('Logout failed:', error); // Fallback: local logout setIsAuthenticated(false); setUser(null); setTokens(null); setSession(null); onAuthLogout?.(); window.location.href = redirectUri || '/'; } }, [onAuthLogout]); // Token yenileme const refreshToken = (0, react_1.useCallback)(async () => { try { const response = await fetch('/api/auth/refresh', { method: 'POST', credentials: 'include' }); if (response.ok) { const newTokens = await response.json(); setTokens(newTokens); if (session) { setSession({ ...session, tokens: newTokens, lastActivity: Date.now() }); } return true; } return false; } catch (error) { console.error('Token refresh failed:', error); return false; } }, [session]); // Token güncelleme const updateToken = (0, react_1.useCallback)(async (minValidity) => { if (!tokens) return false; const now = Date.now(); const expiresIn = tokens.expiresAt - now; if (expiresIn < minValidity) { return await refreshToken(); } return true; }, [tokens, refreshToken]); // Yetki kontrolleri const hasRole = (0, react_1.useCallback)((role, resource) => { if (!user) return false; if (resource) { return user.resourceAccess[resource]?.roles?.includes(role) || false; } return user.realmAccess.roles.includes(role) || user.roles.includes(role); }, [user]); const hasAnyRole = (0, react_1.useCallback)((roles, resource) => { return roles.some(role => hasRole(role, resource)); }, [hasRole]); const hasAllRoles = (0, react_1.useCallback)((roles, resource) => { return roles.every(role => hasRole(role, resource)); }, [hasRole]); const getUserRoles = (0, react_1.useCallback)((resource) => { if (!user) return []; if (resource) { return user.resourceAccess[resource]?.roles || []; } return [...user.realmAccess.roles, ...user.roles]; }, [user]); // Session yönetimi const getSession = (0, react_1.useCallback)(() => { return session; }, [session]); const clearSession = (0, react_1.useCallback)(() => { setIsAuthenticated(false); setUser(null); setTokens(null); setSession(null); }, []); const isSessionValid = (0, react_1.useCallback)(() => { return isAuthenticated && session !== null; }, [isAuthenticated, session]); const value = { isAuthenticated, user, tokens, session, isLoading, error, hasRole, hasAnyRole, hasAllRoles, getUserRoles, login, logout, refreshToken, updateToken, clearSession, isSessionValid }; return ((0, jsx_runtime_1.jsx)(KeycloakContext.Provider, { value: value, children: children })); } /** * useKeycloak hook */ function useKeycloak() { const context = (0, react_1.useContext)(KeycloakContext); if (context === undefined) { throw new Error('useKeycloak must be used within a KeycloakProvider'); } return context; } /** * useKeycloakSession hook - Sadece session bilgileri için */ function useKeycloakSession() { const { session, isAuthenticated, isLoading } = useKeycloak(); return { session, isAuthenticated, isLoading }; } /** * useKeycloakUser hook - Sadece kullanıcı bilgileri için */ function useKeycloakUser() { const { user, isAuthenticated, isLoading } = useKeycloak(); return { user, isAuthenticated, isLoading }; } /** * useKeycloakAuth hook - Sadece authentication fonksiyonları için */ function useKeycloakAuth() { const { login, logout, refreshToken, updateToken } = useKeycloak(); return { login, logout, refreshToken, updateToken }; } /** * useKeycloakRoles hook - Sadece rol kontrolleri için */ function useKeycloakRoles() { const { hasRole, hasAnyRole, hasAllRoles, getUserRoles } = useKeycloak(); return { hasRole, hasAnyRole, hasAllRoles, getUserRoles }; }