mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-17 08:05:19 +00:00
Send redirectOnError param to getPublishedContent when token is pulled from cookie (#2626)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@gitbook/react-contentkit': minor
|
||||
'gitbook': minor
|
||||
---
|
||||
|
||||
Send redirectOnError param to getPublishedContent when token is pulled from cookie
|
||||
@@ -6,20 +6,25 @@ import {
|
||||
CustomizationHeaderPreset,
|
||||
CustomizationIconsStyle,
|
||||
CustomizationLocale,
|
||||
CustomizationSidebarBackgroundStyle,
|
||||
CustomizationSidebarListStyle,
|
||||
CustomizationThemeMode,
|
||||
SiteCustomizationSettings,
|
||||
} from '@gitbook/api';
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
import { test, expect, Page, BrowserContext } from '@playwright/test';
|
||||
import deepMerge from 'deepmerge';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import rison from 'rison';
|
||||
import { DeepPartial } from 'ts-essentials';
|
||||
|
||||
import { getVisitorAuthCookieName, getVisitorAuthCookieValue } from '@/lib/visitor-auth';
|
||||
|
||||
import { getContentTestURL } from '../tests/utils';
|
||||
|
||||
interface Test {
|
||||
name: string;
|
||||
url: string; // URL to visit for testing
|
||||
cookies?: Parameters<BrowserContext['addCookies']>[0];
|
||||
run?: (page: Page) => Promise<unknown>; // The test to run
|
||||
fullPage?: boolean; // Whether the test should be fullscreened during testing
|
||||
screenshot?: false; // Should a screenshot be stored
|
||||
@@ -867,6 +872,60 @@ const testCases: TestsCase[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Visitor Auth - Site (redirects to fallback/auth URL)',
|
||||
baseUrl: `https://gitbook-open-e2e-sites.gitbook.io/va-site-redirects-fallback/`,
|
||||
tests: [
|
||||
{
|
||||
name: 'Redirect to fallback on invalid token pulled from cookie',
|
||||
url: '',
|
||||
screenshot: false,
|
||||
cookies: (() => {
|
||||
const basePath = '/va-site-redirects-fallback/';
|
||||
const invalidToken = jwt.sign(
|
||||
{
|
||||
name: 'gitbook-open-tests',
|
||||
},
|
||||
'invalidKey',
|
||||
{
|
||||
expiresIn: '24h',
|
||||
},
|
||||
);
|
||||
return [
|
||||
{
|
||||
name: getVisitorAuthCookieName(basePath),
|
||||
value: getVisitorAuthCookieValue(basePath, invalidToken),
|
||||
httpOnly: true,
|
||||
},
|
||||
];
|
||||
})(),
|
||||
run: async (page) => {
|
||||
await expect(page).toHaveURL(/https:\/\/www.google.com/);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Show error message when invalid token is passed to url',
|
||||
screenshot: false,
|
||||
url: (() => {
|
||||
const token = jwt.sign(
|
||||
{
|
||||
name: 'gitbook-open-tests',
|
||||
},
|
||||
'invalidKey',
|
||||
{
|
||||
expiresIn: '24h',
|
||||
},
|
||||
);
|
||||
return `?jwt_token=${token}`;
|
||||
})(),
|
||||
run: async (page) => {
|
||||
await expect(page.locator('pre')).toContainText(
|
||||
'Error while validating the JWT token. Reason: The token signature is invalid.',
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Languages',
|
||||
baseUrl: 'https://gitbook.gitbook.io/test-gitbook-open/',
|
||||
@@ -1112,9 +1171,18 @@ for (const testCase of testCases) {
|
||||
test.describe(testCase.name, () => {
|
||||
for (const testEntry of testCase.tests) {
|
||||
const testFn = testEntry.only ? test.only : test;
|
||||
testFn(testEntry.name, async ({ page, baseURL }) => {
|
||||
testFn(testEntry.name, async ({ page, baseURL, context }) => {
|
||||
const contentUrl = new URL(testEntry.url, testCase.baseUrl);
|
||||
const url = getContentTestURL(contentUrl.toString(), baseURL);
|
||||
if (testEntry.cookies) {
|
||||
await context.addCookies(
|
||||
testEntry.cookies.map((cookie) => ({
|
||||
...cookie,
|
||||
domain: new URL(url).host,
|
||||
path: '/',
|
||||
})),
|
||||
);
|
||||
}
|
||||
await page.goto(url);
|
||||
if (testEntry.run) {
|
||||
await testEntry.run(page);
|
||||
@@ -1152,6 +1220,10 @@ function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettings>): s
|
||||
font: CustomizationFont.Inter,
|
||||
background: CustomizationBackground.Plain,
|
||||
icons: CustomizationIconsStyle.Regular,
|
||||
sidebar: {
|
||||
background: CustomizationSidebarBackgroundStyle.Default,
|
||||
list: CustomizationSidebarListStyle.Default,
|
||||
},
|
||||
},
|
||||
internationalization: {
|
||||
locale: CustomizationLocale.En,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gitbook/api": "^0.81.0",
|
||||
"@gitbook/api": "^0.83.0",
|
||||
"@gitbook/cache-do": "workspace:*",
|
||||
"@gitbook/emoji-codepoints": "workspace:*",
|
||||
"@gitbook/icons": "workspace:*",
|
||||
|
||||
@@ -214,6 +214,7 @@ export const getPublishedContentByUrl = cache({
|
||||
get: async (
|
||||
url: string,
|
||||
visitorAuthToken: string | undefined,
|
||||
redirectOnError: boolean | undefined,
|
||||
options: CacheFunctionOptions,
|
||||
) => {
|
||||
try {
|
||||
@@ -221,6 +222,7 @@ export const getPublishedContentByUrl = cache({
|
||||
{
|
||||
url,
|
||||
visitorAuthToken,
|
||||
redirectOnError,
|
||||
},
|
||||
{
|
||||
signal: options.signal,
|
||||
|
||||
@@ -12,6 +12,16 @@ export type VisitorAuthCookieValue = {
|
||||
token: string;
|
||||
};
|
||||
|
||||
export function isVisitorAuthTokenFromCookies(
|
||||
visitorAuthToken: NonNullable<ReturnType<typeof getVisitorAuthToken>>,
|
||||
) {
|
||||
return (
|
||||
typeof visitorAuthToken !== 'string' &&
|
||||
'basePath' in visitorAuthToken &&
|
||||
'token' in visitorAuthToken
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visitor authentication token for the request. This token can either be in the
|
||||
* query parameters or stored as a cookie.
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
getVisitorAuthCookieName,
|
||||
getVisitorAuthCookieValue,
|
||||
getVisitorAuthToken,
|
||||
isVisitorAuthTokenFromCookies,
|
||||
normalizeVisitorAuthURL,
|
||||
} from '@/lib/visitor-auth';
|
||||
|
||||
@@ -685,14 +686,21 @@ async function lookupSiteByAPI(
|
||||
`lookup content for url "${url.toString()}", with ${lookup.urls.length} alternatives`,
|
||||
);
|
||||
|
||||
// When the visitor auth token is pulled from the cookie, set redirectOnError when calling getPublishedContentByUrl to allow
|
||||
// redirecting when the token is invalid as we could be dealing with stale token stored in the cookie.
|
||||
// For example when the VA backend signature has changed but the token stored in the cookie is not yet expired.
|
||||
const redirectOnError =
|
||||
typeof visitorAuthToken !== 'undefined' && isVisitorAuthTokenFromCookies(visitorAuthToken);
|
||||
|
||||
const result = await race(lookup.urls, async (alternative, { signal }) => {
|
||||
const data = await getPublishedContentByUrl(
|
||||
alternative.url,
|
||||
typeof visitorAuthToken === 'undefined'
|
||||
? undefined
|
||||
: typeof visitorAuthToken === 'string'
|
||||
? visitorAuthToken
|
||||
: visitorAuthToken.token,
|
||||
: isVisitorAuthTokenFromCookies(visitorAuthToken)
|
||||
? visitorAuthToken.token
|
||||
: visitorAuthToken,
|
||||
typeof visitorAuthToken === 'undefined' ? undefined : redirectOnError,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.5.1",
|
||||
"@gitbook/api": "^0.77.0",
|
||||
"@gitbook/api": "^0.83.0",
|
||||
"assert-never": "^1.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
Reference in New Issue
Block a user