UNPKG

ssr-keycloak

Version:

SSR compatible Keycloak authentication library for React applications

231 lines (230 loc) 9.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createKeycloakMiddleware = createKeycloakMiddleware; exports.createKeycloakAPIHandler = createKeycloakAPIHandler; exports.withKeycloakSSR = withKeycloakSSR; // SSR Keycloak - Next.js middleware entegrasyonu const server_1 = require("next/server"); const auth_handler_1 = require("../server/auth-handler"); const session_1 = require("../server/session"); /** * Next.js middleware için Keycloak entegrasyonu */ function createKeycloakMiddleware(config, middlewareConfig = {}) { const { protectedRoutes = [], publicRoutes = [], loginRoute = '/login', redirectToLogin = true, cookieName = 'ssr_keycloak_session', cookieSecret = process.env.KEYCLOAK_SESSION_SECRET || 'default-secret' } = middlewareConfig; const authHandler = new auth_handler_1.KeycloakAuthHandler(config); return async function middleware(request) { const { pathname } = request.nextUrl; // Request context oluştur const requestContext = { cookies: Object.fromEntries(request.cookies.getAll().map((cookie) => [cookie.name, cookie.value])), headers: {}, url: request.url, method: request.method }; // Response context oluştur const response = server_1.NextResponse.next(); const responseContext = { setCookie: (cookie) => { response.cookies.set(cookie.name, cookie.value, { expires: cookie.expires, maxAge: cookie.maxAge, domain: cookie.domain, path: cookie.path, secure: cookie.secure, httpOnly: cookie.httpOnly, sameSite: cookie.sameSite }); }, deleteCookie: (name) => { response.cookies.delete(name); }, redirect: (url) => { return server_1.NextResponse.redirect(new URL(url, request.url)); }, json: (data) => { return server_1.NextResponse.json(data); } }; // Public routes kontrolü if (publicRoutes.some(route => pathname.startsWith(route))) { return response; } // API routes kontrolü if (pathname.startsWith('/api/')) { return response; } // Static files kontrolü if (pathname.startsWith('/_next/') || pathname.startsWith('/static/') || pathname.includes('.')) { return response; } // Login route kontrolü if (pathname === loginRoute) { return response; } // Callback route kontrolü if (pathname === '/auth/callback') { try { await authHandler.handleCallback(requestContext, responseContext); return response; } catch (error) { console.error('Callback handling failed:', error); return server_1.NextResponse.redirect(new URL('/login?error=callback_failed', request.url)); } } // Logout route kontrolü if (pathname === '/auth/logout') { try { await authHandler.logout(requestContext, responseContext); return response; } catch (error) { console.error('Logout failed:', error); return server_1.NextResponse.redirect(new URL('/', request.url)); } } // Protected routes kontrolü const isProtectedRoute = protectedRoutes.length === 0 || protectedRoutes.some(route => pathname.startsWith(route)); if (isProtectedRoute) { // Session kontrolü const session = (0, session_1.getSessionFromRequest)(requestContext); if (!session || !(0, session_1.isSessionValid)(session)) { if (redirectToLogin) { const loginUrl = new URL(loginRoute, request.url); loginUrl.searchParams.set('redirect', pathname); return server_1.NextResponse.redirect(loginUrl); } else { return server_1.NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } } // Session'ı response'a ekle (headers olarak) response.headers.set('x-keycloak-session', JSON.stringify({ userId: session.userId, username: session.user.username, roles: session.user.roles, realmAccess: session.user.realmAccess })); } return response; }; } /** * Next.js API route handler'ları için Keycloak entegrasyonu */ function createKeycloakAPIHandler(config, handler) { const authHandler = new auth_handler_1.KeycloakAuthHandler(config); return async function apiHandler(request) { const requestContext = { cookies: Object.fromEntries(request.cookies.getAll().map((cookie) => [cookie.name, cookie.value])), headers: {}, url: request.url, method: request.method }; const response = server_1.NextResponse.next(); const responseContext = { setCookie: (cookie) => { response.cookies.set(cookie.name, cookie.value, { expires: cookie.expires, maxAge: cookie.maxAge, domain: cookie.domain, path: cookie.path, secure: cookie.secure, httpOnly: cookie.httpOnly, sameSite: cookie.sameSite }); }, deleteCookie: (name) => { response.cookies.delete(name); }, redirect: (url) => { return server_1.NextResponse.redirect(new URL(url, request.url)); }, json: (data) => { return server_1.NextResponse.json(data); } }; // Session context oluştur const session = (0, session_1.getSessionFromRequest)(requestContext); const context = { session, isAuthenticated: (0, session_1.isSessionValid)(session), user: session?.user || null, tokens: session?.tokens || null, hasRole: (role, resource) => (0, session_1.hasRoleInSession)(session, role, resource), hasAnyRole: (roles, resource) => (0, session_1.hasAnyRoleInSession)(session, roles, resource), hasAllRoles: (roles, resource) => (0, session_1.hasAllRolesInSession)(session, roles, resource), getUserRoles: (resource) => (0, session_1.getUserRolesFromSession)(session, resource) }; try { return await handler(request, response, context); } catch (error) { console.error('API handler error:', error); return server_1.NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } }; } /** * Server-side props için Keycloak entegrasyonu */ function withKeycloakSSR(config, getServerSideProps) { return async function getServerSidePropsWithKeycloak(context) { const { req, res } = context; const requestContext = { cookies: req.cookies || {}, headers: req.headers || {}, url: req.url || '', method: req.method || 'GET' }; const responseContext = { setCookie: (cookie) => { res.setHeader('Set-Cookie', `${cookie.name}=${cookie.value}; Path=${cookie.path || '/'}; ${cookie.httpOnly ? 'HttpOnly; ' : ''}${cookie.secure ? 'Secure; ' : ''}SameSite=${cookie.sameSite || 'lax'}`); }, deleteCookie: (name) => { res.setHeader('Set-Cookie', `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT`); }, redirect: (url) => { res.writeHead(302, { Location: url }); res.end(); }, json: (data) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(data)); } }; const session = (0, session_1.getSessionFromRequest)(requestContext); const keycloakContext = { session, isAuthenticated: (0, session_1.isSessionValid)(session), user: session?.user || null, tokens: session?.tokens || null, hasRole: (role, resource) => (0, session_1.hasRoleInSession)(session, role, resource), hasAnyRole: (roles, resource) => (0, session_1.hasAnyRoleInSession)(session, roles, resource), hasAllRoles: (roles, resource) => (0, session_1.hasAllRolesInSession)(session, roles, resource), getUserRoles: (resource) => (0, session_1.getUserRolesFromSession)(session, resource) }; if (getServerSideProps) { const props = await getServerSideProps({ ...context, keycloak: keycloakContext }); return { ...props, props: { ...props.props, keycloak: keycloakContext } }; } return { props: { keycloak: keycloakContext } }; }; }