Skip to content

Commit d903273

Browse files
authored
Strip visitor.* param when normalizing URL (#3512)
1 parent d130532 commit d903273

File tree

4 files changed

+60
-14
lines changed

4 files changed

+60
-14
lines changed

.changeset/hungry-ladybugs-allow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Strip visitor params from URL

packages/gitbook/src/lib/visitors.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getVisitorAuthCookieValue,
88
getVisitorToken,
99
getVisitorUnsignedClaims,
10+
normalizeVisitorURL,
1011
} from './visitors';
1112

1213
describe('getVisitorAuthToken', () => {
@@ -295,3 +296,34 @@ describe('getVisitorUnsignedClaims', () => {
295296
});
296297
});
297298
});
299+
300+
describe('normalizeVisitorURL', () => {
301+
it('should strip the jwt_token param when present in the URL', () => {
302+
const url = new URL('https://docs.example.com/?jwt_token=fake-token');
303+
expect(normalizeVisitorURL(url).toString()).toBe('https://docs.example.com/');
304+
});
305+
306+
it('should strip the visitor.* params when present in the URL', () => {
307+
const url = new URL(
308+
'https://docs.example.com/?visitor.isBetaUser=true&visitor.language=fr'
309+
);
310+
expect(normalizeVisitorURL(url).toString()).toBe('https://docs.example.com/');
311+
});
312+
313+
it('should strip both jwt_token and visitor.* params when present in the URL', () => {
314+
const url = new URL(
315+
'https://docs.example.com/?jwt_token=fake-token&visitor.isBetaUser=true&visitor.language=fr'
316+
);
317+
expect(normalizeVisitorURL(url).toString()).toBe('https://docs.example.com/');
318+
});
319+
320+
it('should leave other params like q or ask untouched', () => {
321+
const url1 = new URL('https://docs.example.com/?q=search');
322+
expect(normalizeVisitorURL(url1).toString()).toBe('https://docs.example.com/?q=search');
323+
324+
const url2 = new URL('https://docs.example.com/?ask=this+is+a+question');
325+
expect(normalizeVisitorURL(url2).toString()).toBe(
326+
'https://docs.example.com/?ask=this+is+a+question'
327+
);
328+
});
329+
});

packages/gitbook/src/lib/visitors.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { NextRequest } from 'next/server';
33
import hash from 'object-hash';
44

55
const VISITOR_AUTH_PARAM = 'jwt_token';
6+
const VISITOR_PARAM_PREFIX = 'visitor.';
67
export const VISITOR_TOKEN_COOKIE = 'gitbook-visitor-token';
78
const VISITOR_UNSIGNED_CLAIMS_PREFIX = 'gitbook-visitor-public';
89

@@ -163,8 +164,8 @@ export function getVisitorUnsignedClaims(args: {
163164
}
164165

165166
for (const [key, value] of url.searchParams.entries()) {
166-
if (key.startsWith('visitor.')) {
167-
const claimPath = key.substring('visitor.'.length);
167+
if (key.startsWith(VISITOR_PARAM_PREFIX)) {
168+
const claimPath = key.substring(VISITOR_PARAM_PREFIX.length);
168169
const claimValue = parseVisitorQueryParamValue(value);
169170

170171
setVisitorClaimByPath(claims, claimPath, claimValue);
@@ -326,16 +327,21 @@ export function getVisitorAuthCookieValue(basePath: string, token: string): stri
326327
}
327328

328329
/**
329-
* Normalize the URL by removing the visitor authentication token from the query parameters (if present).
330+
* Normalize the URL by removing the visitor JWT token and visitor.* param from the query parameters (if present).
330331
*/
331-
export function normalizeVisitorAuthURL(url: URL): URL {
332+
export function normalizeVisitorURL(url: URL): URL {
333+
const withoutVisitorParamsURL = new URL(url);
332334
if (url.searchParams.has(VISITOR_AUTH_PARAM)) {
333-
const withoutVAParam = new URL(url);
334-
withoutVAParam.searchParams.delete(VISITOR_AUTH_PARAM);
335-
return withoutVAParam;
335+
withoutVisitorParamsURL.searchParams.delete(VISITOR_AUTH_PARAM);
336336
}
337337

338-
return url;
338+
for (const [urlParam] of url.searchParams.entries()) {
339+
if (urlParam.startsWith(VISITOR_PARAM_PREFIX)) {
340+
withoutVisitorParamsURL.searchParams.delete(urlParam);
341+
}
342+
}
343+
344+
return withoutVisitorParamsURL;
339345
}
340346

341347
/**

packages/gitbook/src/middleware.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
getPathScopedCookieName,
2222
getResponseCookiesForVisitorAuth,
2323
getVisitorData,
24-
normalizeVisitorAuthURL,
24+
normalizeVisitorURL,
2525
} from '@/lib/visitors';
2626
import { serveResizedImage } from '@/routes/image';
2727
import { cookies } from 'next/headers';
@@ -221,13 +221,16 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
221221
incomingURL.search = requestURL.search;
222222
}
223223
//
224-
// Make sure the URL is clean of any va token after a successful lookup
225-
// The token is stored in a cookie that is set on the redirect response
224+
// Make sure the URL is clean of any va token after a successful lookup,
225+
// and of any visitor.* params that may have been passed to the URL.
226226
//
227-
const incomingURLWithoutToken = normalizeVisitorAuthURL(incomingURL);
228-
if (incomingURLWithoutToken.toString() !== incomingURL.toString()) {
227+
// The token and the visitor.* params value are stored in cookies that are set
228+
// on the redirect response.
229+
//
230+
const normalizedVisitorURL = normalizeVisitorURL(incomingURL);
231+
if (normalizedVisitorURL.toString() !== incomingURL.toString()) {
229232
return writeResponseCookies(
230-
NextResponse.redirect(incomingURLWithoutToken.toString()),
233+
NextResponse.redirect(normalizedVisitorURL.toString()),
231234
cookies
232235
);
233236
}

0 commit comments

Comments
 (0)