From 4ac29817a26e8917bb7b395c3aee53bac3ccc14a Mon Sep 17 00:00:00 2001 From: spastorelli Date: Mon, 16 Mar 2026 16:00:31 +0100 Subject: [PATCH] Expose a ~gitbook/auth/login URL to handle redirection to upstream auth with current location passed (#4113) --- bun.lock | 5 +- package.json | 2 +- .../[siteData]/~gitbook/auth/login/route.ts | 30 +++++ .../components/DocumentView/InlineButton.tsx | 37 +++--- .../src/components/Header/HeaderLink.tsx | 119 +++++++++++------- .../src/components/Header/HeaderLinkMore.tsx | 35 +++--- .../primitives/SiteAuthLoginLink.tsx | 76 +++++++++++ .../gitbook/src/lib/auth-login-link.test.ts | 85 +++++++++++++ packages/gitbook/src/lib/auth-login-link.ts | 36 ++++++ packages/gitbook/src/lib/sites.ts | 8 +- packages/gitbook/src/middleware.ts | 3 +- 11 files changed, 354 insertions(+), 82 deletions(-) create mode 100644 packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/login/route.ts create mode 100644 packages/gitbook/src/components/primitives/SiteAuthLoginLink.tsx create mode 100644 packages/gitbook/src/lib/auth-login-link.test.ts create mode 100644 packages/gitbook/src/lib/auth-login-link.ts diff --git a/bun.lock b/bun.lock index a88481850..a287b4da3 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "gitbook", @@ -349,7 +348,7 @@ "react-dom": "catalog:", }, "catalog": { - "@gitbook/api": "0.169.0", + "@gitbook/api": "0.170.0", "@scalar/api-client-react": "^1.3.46", "@tsconfig/node20": "^20.1.6", "@tsconfig/strictest": "^2.0.6", @@ -746,7 +745,7 @@ "@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA=="], - "@gitbook/api": ["@gitbook/api@0.169.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-kzJT8P5HCnqeYG3kFmBfvKMjQhTuUVmNV5Wvyh87QnoOA3odwh2C3qONpTmBA6cNE3ymDnxs8SJT9FG/qquRZQ=="], + "@gitbook/api": ["@gitbook/api@0.170.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-RCW9tipg8CJqXPHrZ65wj/EwN4/Rmvy4uOZ8tez5B9NcM7C4vYJOBcgsBeGD4bB5I0YmH/uyZMno5zgIM8sOKw=="], "@gitbook/browser-types": ["@gitbook/browser-types@workspace:packages/browser-types"], diff --git a/package.json b/package.json index 163b103ed..4b2e0ccfb 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "catalog": { "@tsconfig/strictest": "^2.0.6", "@tsconfig/node20": "^20.1.6", - "@gitbook/api": "0.169.0", + "@gitbook/api": "0.170.0", "@scalar/api-client-react": "^1.3.46", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", 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 new file mode 100644 index 000000000..3e2362db8 --- /dev/null +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/~gitbook/auth/login/route.ts @@ -0,0 +1,30 @@ +import { type RouteLayoutParams, getDynamicSiteContext } from '@/app/utils'; +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; + +/** + * Redirect to the upstream auth provider login URL of site, or to the site root when not configured. + */ +export async function GET( + request: NextRequest, + { params }: { params: Promise } +) { + const { context } = await getDynamicSiteContext(await params); + const noLoginFallbackURL = context.linker.toAbsoluteURL(context.linker.toPathInSite('')); + + 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); + } +} diff --git a/packages/gitbook/src/components/DocumentView/InlineButton.tsx b/packages/gitbook/src/components/DocumentView/InlineButton.tsx index 29a4b4e19..5e9e6f956 100644 --- a/packages/gitbook/src/components/DocumentView/InlineButton.tsx +++ b/packages/gitbook/src/components/DocumentView/InlineButton.tsx @@ -1,7 +1,10 @@ +import { isSiteAuthLoginHref } from '@/lib/auth-login-link'; import { resolveContentRefFallback, resolveContentRefInDocument } from '@/lib/references'; import * as api from '@gitbook/api'; import type { IconName } from '@gitbook/icons'; +import type React from 'react'; import { Button, type ButtonProps } from '../primitives'; +import { SiteAuthLoginButton } from '../primitives/SiteAuthLoginLink'; import type { InlineProps } from './Inline'; import { InlineActionButton } from './InlineActionButton'; import { NotFoundRefHoverCard } from './NotFoundRefHoverCard'; @@ -58,21 +61,27 @@ export async function InlineLinkButton( const href = resolved?.href ?? (inline.data.ref ? resolveContentRefFallback(inline.data.ref)?.href : undefined); + const sharedProps: React.ComponentProps = { + ...buttonProps, + insights: { + type: 'link_click' as const, + link: { + target: inline.data.ref, + position: api.SiteInsightsLinkPosition.Content, + }, + }, + href, + disabled: href === undefined, + }; - const button = ( -