Add ~gitbook/auth/logout endpoint in GBO (#4195)

This commit is contained in:
spastorelli
2026-04-16 22:40:56 +02:00
committed by GitHub
parent b2854500a1
commit 045f603849
5 changed files with 50 additions and 26 deletions
@@ -0,0 +1,36 @@
import {
type RouteLayoutParams,
getDynamicSiteContext,
getSiteURLDataFromParams,
} from '@/app/utils';
import { getVisitorAuthBasePath } from '@/lib/data';
import { getVisitorAuthCookieName } from '@/lib/visitors';
import { cookies } from 'next/headers';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
/**
* Clear the site-scoped auth session cookies and redirect to the site root.
*/
export async function GET(
request: NextRequest,
{ params }: { params: Promise<RouteLayoutParams> }
) {
const resolvedParams = await params;
const [{ context }, siteURLData, cookieStore] = await Promise.all([
getDynamicSiteContext(resolvedParams),
getSiteURLDataFromParams(resolvedParams),
cookies(),
]);
cookieStore.delete(
getVisitorAuthCookieName(
getVisitorAuthBasePath(new URL(request.nextUrl.toString()), siteURLData)
)
);
// 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('')));
}
+1 -1
View File
@@ -119,7 +119,7 @@ function getModeFromParams(mode: string): RouteParamMode {
/**
* Get the decoded site data from the params.
*/
function getSiteURLDataFromParams(params: RouteLayoutParams): SiteURLData {
export function getSiteURLDataFromParams(params: RouteLayoutParams): SiteURLData {
try {
const decoded = decodeURIComponent(params.siteData);
return rison.decode(decoded);
@@ -7,16 +7,7 @@ describe('getVisitorAuthBasePath', () => {
getVisitorAuthBasePath(
new URL('https://proxy.gitbook.site/sites/site_foo/hello/world'),
{
site: 'site_foo',
siteSpace: 'sitesp_foo',
basePath: '/foo',
siteBasePath: '/foo',
organization: 'org_foo',
space: 'space_foo',
pathname: '/hello/world',
complete: false,
apiToken: 'api_token_foo',
canonicalUrl: 'https://example.com/docs/foo/hello/world',
}
)
).toBe('/sites/site_foo/');
@@ -25,16 +16,7 @@ describe('getVisitorAuthBasePath', () => {
it('should return the correct base path for non-proxy requests', () => {
expect(
getVisitorAuthBasePath(new URL('https://example.com/docs/foo/hello/world'), {
site: 'site_foo',
siteSpace: 'sitesp_foo',
basePath: '/foo/',
siteBasePath: '/foo/',
organization: 'org_foo',
space: 'space_foo',
pathname: '/hello/world',
complete: false,
apiToken: 'api_token_foo',
canonicalUrl: 'https://example.com/docs/foo/hello/world',
})
).toBe('/foo/');
});
+1 -1
View File
@@ -7,7 +7,7 @@ import type { PublishedSiteContent } from '@gitbook/api';
*/
export function getVisitorAuthBasePath(
siteRequestURL: URL,
siteURLData: PublishedSiteContent
siteURLData: Pick<PublishedSiteContent, 'siteBasePath'>
): string {
// The siteRequestURL for proxy requests is of the form `https://proxy.gitbook.com/site/siteId/...`
// In such cases, we should not use the resolved siteBasePath for the cookie because for subsequent requests
+12 -6
View File
@@ -4,6 +4,7 @@ import {
SiteInsightsLLMSVariant,
} from '@gitbook/api';
import { shouldServeMarkdown } from '@vercel/agent-readability';
import { cookies } from 'next/headers';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import rison from 'rison';
@@ -43,7 +44,6 @@ import {
} from '@/lib/visitors';
import { waitUntil } from '@/lib/waitUntil';
import { serveResizedImage } from '@/routes/image';
import { cookies } from 'next/headers';
import {
type ServerInsightsEventInput,
serveProxyAnalyticsEvent,
@@ -305,12 +305,17 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
});
}
cookies.push(
...getResponseCookiesForVisitorAuth(
getVisitorAuthBasePath(siteRequestURL, siteURLData),
visitorToken
)
const normalizedSitePathname = removeLeadingSlash(
removeTrailingSlash(siteURLData.pathname)
);
if (normalizedSitePathname !== '~gitbook/auth/logout') {
cookies.push(
...getResponseCookiesForVisitorAuth(
getVisitorAuthBasePath(siteRequestURL, siteURLData),
visitorToken
)
);
}
// We use the host/origin from the canonical URL to ensure the links are
// correctly generated when the site is proxied. e.g. https://proxy.gitbook.com/site/siteId/...
@@ -755,6 +760,7 @@ function encodePathInSiteContent(
case '~gitbook/pdf':
case '~gitbook/search':
case '~gitbook/auth/login':
case '~gitbook/auth/logout':
case '~scalar/proxy':
// PDF, search and auth routes are always dynamic as they depend on the request.
return { pathname, routeType: 'dynamic' };