ssr-keycloak
Version:
SSR compatible Keycloak authentication library for React applications
283 lines (282 loc) • 10.1 kB
JavaScript
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 };
}
;