diff --git a/server/utils/oidc-provider.ts b/server/utils/oidc-provider.ts index 25edceb..dfc7042 100644 --- a/server/utils/oidc-provider.ts +++ b/server/utils/oidc-provider.ts @@ -93,34 +93,43 @@ export async function getOidcProvider() { rpInitiatedLogout: { enabled: true, logoutSource: async (ctx: any, form: string) => { - // oidc-provider's form HTML is a stable format (see node_modules/ - // oidc-provider/lib/actions/end_session.js:90): - //
- // We extract just the xsrf token and hand off to a Nuxt page at - // /auth/logout-confirm that renders a styled form posting back to - // /oidc/session/end/confirm with that xsrf value. The token rides - // in a short-lived httpOnly cookie so it never hits the URL. - const match = form.match(/name="xsrf"\s+value="([^"]+)"/); - if (!match) { - // Defensive: if oidc-provider ever changes its form format, fall - // back to the raw form so logout still works. - ctx.type = "html"; - ctx.status = 200; - ctx.body = `${form}`; - return; - } - ctx.cookies.set("oidc_logout_xsrf", match[1], { + // Auto-submit oidc-provider's own form so the xsrf value stays + // inside the same request cycle that generated it. The previous + // approach extracted the xsrf into a separate cookie and bounced + // through a Nuxt page for a "are you sure?" confirmation, which + // kept desyncing and producing "xsrf token invalid" errors. + // Clicking sign-out in the wiki is already confirmation enough. + ctx.type = "html"; + ctx.status = 200; + ctx.body = ` + + +Signing you out…
+ ${form} + + +`; + }, + postLogoutSuccessSource: async (ctx: any) => { + // Kill the Ghost Guild session cookie so the user is fully signed + // out, not just logged out of Outline. + ctx.cookies.set("auth-token", null, { httpOnly: true, sameSite: "lax", - maxAge: 120_000, // 2 minutes path: "/", overwrite: true, signed: false, }); - ctx.redirect("/auth/logout-confirm"); - }, - postLogoutSuccessSource: async (ctx: any) => { ctx.redirect("/auth/logout-success"); }, },