Redirect to the upstream auth logout URL on ~gitbook/auth/logout (#4489)

This commit is contained in:
spastorelli
2026-08-18 10:27:48 +02:00
committed by GitHub
parent 048c4e4c70
commit fd070ce9ca
3 changed files with 44 additions and 20 deletions
@@ -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);
}
@@ -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);
}
@@ -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();
}