From fd070ce9ca1cd1c27f160951ac9f243292292e6f Mon Sep 17 00:00:00 2001 From: spastorelli Date: Tue, 18 Aug 2026 10:27:48 +0200 Subject: [PATCH] Redirect to the upstream auth logout URL on ~gitbook/auth/logout (#4489) --- .../[siteData]/~gitbook/auth/login/route.ts | 21 +++++----------- .../[siteData]/~gitbook/auth/logout/route.ts | 18 +++++++++---- packages/gitbook/src/lib/site-auth-urls.ts | 25 +++++++++++++++++++ 3 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 packages/gitbook/src/lib/site-auth-urls.ts diff --git a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/login/route.ts b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/login/route.ts index 7f6c80eea..c6bbf09aa 100644 --- a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/login/route.ts +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/login/route.ts @@ -2,6 +2,7 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { type RouteLayoutParams, getDynamicSiteContext } from '@/app/utils'; +import { resolveUpstreamAuthURL } from '@/lib/site-auth-urls'; /** * Redirect to the upstream auth provider login URL of site, or to the site root when not configured. @@ -12,20 +13,10 @@ export async function GET( ) { const { context } = await getDynamicSiteContext(await params); const noLoginFallbackURL = context.linker.toAbsoluteURL(context.linker.toPathInSite('')); + const loginURL = resolveUpstreamAuthURL({ + siteAuthURL: context.site.urls.login, + location: request.nextUrl.searchParams.get('location'), + }); - if (!context.site.urls.login) { - return NextResponse.redirect(noLoginFallbackURL); - } - - try { - const loginURL = new URL(context.site.urls.login); - const location = request.nextUrl.searchParams.get('location'); - if (location) { - loginURL.searchParams.set('location', location); - } - - return NextResponse.redirect(loginURL); - } catch (_error) { - return NextResponse.redirect(noLoginFallbackURL); - } + return NextResponse.redirect(loginURL ?? noLoginFallbackURL); } diff --git a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/logout/route.ts b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/logout/route.ts index 855d7d9b8..ee8750daf 100644 --- a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/logout/route.ts +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/logout/route.ts @@ -8,10 +8,12 @@ import { getSiteURLDataFromParams, } from '@/app/utils'; import { getVisitorAuthBasePath } from '@/lib/data'; +import { resolveUpstreamAuthURL } from '@/lib/site-auth-urls'; import { getVisitorAuthCookieName } from '@/lib/visitors'; /** - * Clear the site-scoped auth session cookies and redirect to the site root. + * Clear the site-scoped auth session cookies and redirect to the upstream auth provider + * logout URL of the site, or to the site root when not configured. */ export async function GET( request: NextRequest, @@ -30,8 +32,14 @@ export async function GET( ) ); - // TODO: Redirect to the site root for now. Once the API supports it, - // optionally redirect to a logoutURL (e.g when needing to logout from upstream auth too) - // when defined in visitor auth settings. - return NextResponse.redirect(context.linker.toAbsoluteURL(context.linker.toPathInSite(''))); + const noLogoutFallbackURL = context.linker.toAbsoluteURL(context.linker.toPathInSite('')); + const logoutURL = resolveUpstreamAuthURL({ + siteAuthURL: context.site.urls.logout, + // Default the location to the site root, so the upstream logout can send the visitor back + // to the site. On a site behind visitor auth, coming back re-enters the login flow and + // surfaces the upstream login page, as the visitor no longer has a session on either side. + location: request.nextUrl.searchParams.get('location') ?? '/', + }); + + return NextResponse.redirect(logoutURL ?? noLogoutFallbackURL); } diff --git a/packages/gitbook/src/lib/site-auth-urls.ts b/packages/gitbook/src/lib/site-auth-urls.ts new file mode 100644 index 000000000..d7dd3a250 --- /dev/null +++ b/packages/gitbook/src/lib/site-auth-urls.ts @@ -0,0 +1,25 @@ +/** + * Resolve the URL to redirect a visitor to for an upstream auth flow (login or logout), + * carrying the site-relative location the visitor should land on once the flow completes. + * + * Returns `null` when the site has no URL configured for the flow, or when it is not a + * valid absolute URL, so callers can fall back to a chosen default URL. + */ +export function resolveUpstreamAuthURL(args: { + siteAuthURL: string | undefined; + location?: string | null; +}): string | null { + const { siteAuthURL, location } = args; + + if (!siteAuthURL || !URL.canParse(siteAuthURL)) { + return null; + } + + const url = new URL(siteAuthURL); + + if (location) { + url.searchParams.set('location', location); + } + + return url.toString(); +}