From aacf7dbbbaadccef51244020e97a83f0dcb2ed6e Mon Sep 17 00:00:00 2001 From: "Zachariah K. Sharma" Date: Sun, 14 Jun 2026 17:55:56 -0600 Subject: [PATCH] fix: add /api/auth/signout route and sign-out link to scheduler - New GET/POST handler at /api/auth/signout deletes the DB session and clears all NextAuth cookies (respecting NEXTAUTH_COOKIE_DOMAIN so the shared .internal.vyntehome.com cookies are properly evicted) - Added NEXTAUTH_COOKIE_DOMAIN to scheduler service env in docker-compose - Sign-out link in AppShell profile block so logout is accessible from the UI --- apps/scheduler/app/api/auth/signout/route.ts | 48 ++++++++++++++++++++ apps/scheduler/app/globals.css | 11 +++++ apps/scheduler/components/AppShell.tsx | 1 + docker-compose.yml | 1 + 4 files changed, 61 insertions(+) create mode 100644 apps/scheduler/app/api/auth/signout/route.ts diff --git a/apps/scheduler/app/api/auth/signout/route.ts b/apps/scheduler/app/api/auth/signout/route.ts new file mode 100644 index 0000000000..49b5af4738 --- /dev/null +++ b/apps/scheduler/app/api/auth/signout/route.ts @@ -0,0 +1,48 @@ +import { cookies } from "next/headers"; +import { NextResponse } from "next/server"; +import prisma from "@calcom/prisma"; + +const AUTH_COOKIE_NAMES = [ + "next-auth.session-token", + "__Secure-next-auth.session-token", + "next-auth.callback-url", + "next-auth.csrf-token", + "__Host-next-auth.csrf-token", +]; + +function baseUrl(): string { + const nextAuthUrl = process.env.NEXTAUTH_URL ?? ""; + try { + return new URL(nextAuthUrl).origin; + } catch { + return "/"; + } +} + +async function handleSignOut(): Promise { + const cookieStore = await cookies(); + + const sessionToken = + cookieStore.get("__Secure-next-auth.session-token")?.value ?? + cookieStore.get("next-auth.session-token")?.value; + + if (sessionToken) { + await prisma.session.deleteMany({ where: { sessionToken } }).catch(() => null); + } + + const res = NextResponse.redirect(new URL("/", baseUrl()), { status: 302 }); + + const cookieDomain = process.env.NEXTAUTH_COOKIE_DOMAIN || undefined; + for (const name of AUTH_COOKIE_NAMES) { + res.cookies.set(name, "", { + maxAge: 0, + path: "/", + ...(cookieDomain ? { domain: cookieDomain } : {}), + }); + } + + return res; +} + +export const GET = handleSignOut; +export const POST = handleSignOut; diff --git a/apps/scheduler/app/globals.css b/apps/scheduler/app/globals.css index 31234c9c1e..a9d4387e9a 100644 --- a/apps/scheduler/app/globals.css +++ b/apps/scheduler/app/globals.css @@ -183,6 +183,17 @@ input[type="number"]::-webkit-inner-spin-button { filter: invert(0.5); } letter-spacing: 0.04em; } +.signout-link { + display: block; + margin-top: 10px; + font-size: 10.5px; + color: var(--text-3); + text-decoration: none; + letter-spacing: 0.04em; + transition: color 0.15s; +} +.signout-link:hover { color: var(--text-2); } + .main-surface { padding: 28px 26px; min-width: 0; diff --git a/apps/scheduler/components/AppShell.tsx b/apps/scheduler/components/AppShell.tsx index 36c73ddd48..44f5a56770 100644 --- a/apps/scheduler/components/AppShell.tsx +++ b/apps/scheduler/components/AppShell.tsx @@ -42,6 +42,7 @@ export function AppShell({
{user.name} {user.timeZone} + Sign out
{children}
diff --git a/docker-compose.yml b/docker-compose.yml index 9cf0b4c63a..c9ecd8cc85 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -150,6 +150,7 @@ services: NODE_ENV: production NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} NEXTAUTH_URL: ${NEXTAUTH_URL} + NEXTAUTH_COOKIE_DOMAIN: ${NEXTAUTH_COOKIE_DOMAIN:-} DATABASE_URL: postgresql://${POSTGRES_USER:-calcom}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-calcom} DATABASE_DIRECT_URL: postgresql://${POSTGRES_USER:-calcom}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-calcom} CALENDSO_ENCRYPTION_KEY: ${CALENDSO_ENCRYPTION_KEY}