Logout (todo §4); GET /logout clears plainpages_jwt + revokes the Kratos session (createLogoutFlow → redirect to Kratos logout URL → /login); wire shell Sign out link

This commit is contained in:
2026-06-18 10:35:07 +02:00
parent 4f6b60463b
commit dec55f85a6
9 changed files with 67 additions and 6 deletions

View File

@@ -13,7 +13,7 @@ import { resolveSession, type VerifyOptions } from "./jwt-middleware.ts";
import type { KetoClient } from "./keto-client.ts";
import type { KratosAdmin } from "./kratos-admin.ts";
import { KratosError, type KratosPublic } from "./kratos-public.ts";
import { completeLogin, remintSession, sessionCookie } from "./login.ts";
import { clearSessionCookie, completeLogin, remintSession, sessionCookie } from "./login.ts";
import { DEFAULT_MENU, type MenuConfig } from "./menu-config.ts";
import type { Plugin, RouteResult } from "./plugin.ts";
import { allowedMethods, isAuthorized, matchRoute } from "./router.ts";
@@ -157,6 +157,16 @@ export function createApp(options: AppOptions = {}): Server {
return;
}
// Logout: clear our local JWT and revoke the Kratos session. Kratos' own cookie lives on
// its origin, so we can't clear it here — redirect the browser to Kratos' logout URL (it
// revokes the session, clears plainpages_session, then lands on /login per kratos.yml).
// No active session ⇒ just clear our cookie and go to /login.
if (pathname === "/logout" && (method === "GET" || method === "HEAD") && kratos) {
const flow = await kratos.createLogoutFlow(req.headers.cookie ? { cookie: req.headers.cookie } : {});
res.writeHead(303, { location: flow?.logoutUrl ?? "/login", "set-cookie": clearSessionCookie() }).end();
return;
}
if (pathname === "/" && (method === "GET" || method === "HEAD")) {
// Roles from the verified JWT (anonymous ⇒ []); branding/override come from config/menu.ts.
sendHtml(res, 200, await render("index", { model: buildDashboardModel(ctx.url, ctx.roles, menu) }));