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