Skip to content

Commit 4ae79af

Browse files
brkalowLekoArts
andauthored
fix(nextjs): url-based session syncing for auth() helpers (clerk#3334)
Co-authored-by: Lennart <lekoarts@gmail.com>
1 parent 39265d9 commit 4ae79af

4 files changed

Lines changed: 53 additions & 6 deletions

File tree

.changeset/late-readers-pretend.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/backend': patch
3+
'@clerk/nextjs': patch
4+
---
5+
6+
Pass `devBrowserToken` to `createRedirect()` to ensure methods from `auth()` that trigger redirects correctly pass the dev browser token for URL-based session syncing.

packages/backend/src/__tests__/createRedirect.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,5 +244,23 @@ export default (QUnit: QUnit) => {
244244
),
245245
);
246246
});
247+
248+
test('passed dev browser when cross-origin redirect in dev', assert => {
249+
const redirectAdapterSpy = sinon.spy(_url => 'redirectAdapterValue');
250+
const { redirectToSignUp } = createRedirect({
251+
baseUrl: 'http://www.clerk.com',
252+
devBrowserToken: 'deadbeef',
253+
redirectAdapter: redirectAdapterSpy,
254+
publishableKey: 'pk_test_aW5jbHVkZWQua2F0eWRpZC05Mi5jbGVyay5hY2NvdW50cy5kZXYk',
255+
});
256+
257+
const result = redirectToSignUp({ returnBackUrl });
258+
assert.equal(result, 'redirectAdapterValue');
259+
assert.ok(
260+
redirectAdapterSpy.calledWith(
261+
`https://included.katydid-92.accounts.dev/sign-up?redirect_url=${encodedUrl}&__clerk_db_jwt=deadbeef`,
262+
),
263+
);
264+
});
247265
});
248266
};

packages/backend/src/createRedirect.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import { constants } from './constants';
12
import { errorThrower, parsePublishableKey } from './util/shared';
23

3-
const buildUrl = (_baseUrl: string | URL, _targetUrl: string | URL, _returnBackUrl?: string | URL | null) => {
4+
const buildUrl = (
5+
_baseUrl: string | URL,
6+
_targetUrl: string | URL,
7+
_returnBackUrl?: string | URL | null,
8+
_devBrowserToken?: string | null,
9+
) => {
410
if (_baseUrl === '') {
511
return legacyBuildUrl(_targetUrl.toString(), _returnBackUrl?.toString());
612
}
@@ -12,6 +18,10 @@ const buildUrl = (_baseUrl: string | URL, _targetUrl: string | URL, _returnBackU
1218
if (returnBackUrl) {
1319
res.searchParams.set('redirect_url', returnBackUrl.toString());
1420
}
21+
// For cross-origin redirects, we need to pass the dev browser token for URL session syncing
22+
if (_devBrowserToken && baseUrl.hostname !== res.hostname) {
23+
res.searchParams.set(constants.Cookies.DevBrowser, _devBrowserToken);
24+
}
1525
return res.toString();
1626
};
1727

@@ -66,6 +76,7 @@ export type RedirectFun<ReturnType> = (params?: RedirectToParams) => ReturnType;
6676
*/
6777
type CreateRedirect = <ReturnType>(params: {
6878
publishableKey: string;
79+
devBrowserToken?: string;
6980
redirectAdapter: RedirectAdapter<ReturnType>;
7081
baseUrl: URL | string;
7182
signInUrl?: URL | string;
@@ -77,23 +88,29 @@ type CreateRedirect = <ReturnType>(params: {
7788

7889
export const createRedirect: CreateRedirect = params => {
7990
const { publishableKey, redirectAdapter, signInUrl, signUpUrl, baseUrl } = params;
80-
const frontendApi = parsePublishableKey(publishableKey)?.frontendApi;
91+
const parsedPublishableKey = parsePublishableKey(publishableKey);
92+
const frontendApi = parsedPublishableKey?.frontendApi;
93+
const isDevelopment = parsedPublishableKey?.instanceType === 'development';
8194
const accountsBaseUrl = buildAccountsBaseUrl(frontendApi);
8295

8396
const redirectToSignUp = ({ returnBackUrl }: RedirectToParams = {}) => {
8497
if (!signUpUrl && !accountsBaseUrl) {
8598
errorThrower.throwMissingPublishableKeyError();
8699
}
87100
const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
88-
return redirectAdapter(buildUrl(baseUrl, signUpUrl || accountsSignUpUrl, returnBackUrl));
101+
return redirectAdapter(
102+
buildUrl(baseUrl, signUpUrl || accountsSignUpUrl, returnBackUrl, isDevelopment ? params.devBrowserToken : null),
103+
);
89104
};
90105

91106
const redirectToSignIn = ({ returnBackUrl }: RedirectToParams = {}) => {
92107
if (!signInUrl && !accountsBaseUrl) {
93108
errorThrower.throwMissingPublishableKeyError();
94109
}
95110
const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
96-
return redirectAdapter(buildUrl(baseUrl, signInUrl || accountsSignInUrl, returnBackUrl));
111+
return redirectAdapter(
112+
buildUrl(baseUrl, signInUrl || accountsSignInUrl, returnBackUrl, isDevelopment ? params.devBrowserToken : null),
113+
);
97114
};
98115

99116
return { redirectToSignUp, redirectToSignIn };

packages/nextjs/src/app-router/server/auth.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { AuthObject, RedirectFun } from '@clerk/backend/internal';
2-
import { createClerkRequest, createRedirect } from '@clerk/backend/internal';
2+
import { constants, createClerkRequest, createRedirect } from '@clerk/backend/internal';
33
import { notFound, redirect } from 'next/navigation';
44

55
import { buildClerkProps } from '../../server/buildClerkProps';
@@ -23,9 +23,15 @@ export const auth = (): Auth => {
2323
const clerkUrl = getAuthKeyFromRequest(request, 'ClerkUrl');
2424

2525
const redirectToSignIn: RedirectFun<never> = (opts = {}) => {
26+
const clerkRequest = createClerkRequest(request);
27+
const devBrowserToken =
28+
clerkRequest.clerkUrl.searchParams.get(constants.Cookies.DevBrowser) ||
29+
clerkRequest.cookies.get(constants.Cookies.DevBrowser);
30+
2631
return createRedirect({
2732
redirectAdapter: redirect,
28-
baseUrl: createClerkRequest(request).clerkUrl.toString(),
33+
devBrowserToken: devBrowserToken,
34+
baseUrl: clerkRequest.clerkUrl.toString(),
2935
// TODO: Support runtime-value configuration of these options
3036
// via setting and reading headers from clerkMiddleware
3137
publishableKey: PUBLISHABLE_KEY,

0 commit comments

Comments
 (0)