From 3c9aacff2276d01cca6eafe63a14b8af0adae10e Mon Sep 17 00:00:00 2001 From: brkalow Date: Wed, 15 Jul 2026 23:09:18 -0500 Subject: [PATCH 1/2] Use FAPI for OAuth popup callbacks --- .changeset/tidy-moose-travel.md | 5 + .../clerk-js/src/core/resources/SignIn.ts | 8 +- .../clerk-js/src/core/resources/SignUp.ts | 8 +- .../core/resources/__tests__/SignIn.test.ts | 19 ++- .../core/resources/__tests__/SignUp.test.ts | 17 ++- .../__tests__/authenticateWithPopup.test.ts | 141 ++++++++++++++++++ .../src/utils/authenticateWithPopup.ts | 63 +++++--- 7 files changed, 230 insertions(+), 31 deletions(-) create mode 100644 .changeset/tidy-moose-travel.md create mode 100644 packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts diff --git a/.changeset/tidy-moose-travel.md b/.changeset/tidy-moose-travel.md new file mode 100644 index 00000000000..a2dcd6cdeb9 --- /dev/null +++ b/.changeset/tidy-moose-travel.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Use the Frontend API for OAuth popup callbacks so popup authentication no longer depends on the Account Portal hostname. diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 65a72ef1c92..d475d449f9b 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -1213,6 +1213,7 @@ class SignInFuture implements SignInFutureResource { } const routes = { redirectUrl: SignIn.clerk.buildUrlWithAuth(redirectCallbackUrl), actionCompleteRedirectUrl }; + let popupState = ''; if (popup) { const wrappedRoutes = wrapWithPopupRoutes(SignIn.clerk, { redirectCallbackUrl: routes.redirectUrl, @@ -1220,6 +1221,7 @@ class SignInFuture implements SignInFutureResource { }); routes.redirectUrl = wrappedRoutes.redirectCallbackUrl; routes.actionCompleteRedirectUrl = wrappedRoutes.redirectUrl; + popupState = wrappedRoutes.state; } // Reuse the existing sign-in by default so any state already attached to it carries @@ -1259,7 +1261,11 @@ class SignInFuture implements SignInFutureResource { if (status === 'unverified' && externalVerificationRedirectURL) { if (popup) { - await _futureAuthenticateWithPopup(SignIn.clerk, { popup, externalVerificationRedirectURL }); + await _futureAuthenticateWithPopup(SignIn.clerk, { + popup, + externalVerificationRedirectURL, + state: popupState, + }); // Pick up the modified SignIn resource await this.#resource.reload(); } else { diff --git a/packages/clerk-js/src/core/resources/SignUp.ts b/packages/clerk-js/src/core/resources/SignUp.ts index 2a4ed453c3d..a310399110b 100644 --- a/packages/clerk-js/src/core/resources/SignUp.ts +++ b/packages/clerk-js/src/core/resources/SignUp.ts @@ -1074,6 +1074,7 @@ class SignUpFuture implements SignUpFutureResource { redirectUrl: SignUp.clerk.buildUrlWithAuth(redirectCallbackUrl), actionCompleteRedirectUrl: redirectUrlComplete, }; + let popupState = ''; if (popup) { const wrappedRoutes = wrapWithPopupRoutes(SignUp.clerk, { redirectCallbackUrl: routes.redirectUrl, @@ -1081,6 +1082,7 @@ class SignUpFuture implements SignUpFutureResource { }); routes.redirectUrl = wrappedRoutes.redirectCallbackUrl; routes.actionCompleteRedirectUrl = wrappedRoutes.redirectUrl; + popupState = wrappedRoutes.state; } const authenticateFn = () => { @@ -1119,7 +1121,11 @@ class SignUpFuture implements SignUpFutureResource { if (status === 'unverified' && externalVerificationRedirectURL) { if (popup) { - await _futureAuthenticateWithPopup(SignUp.clerk, { popup, externalVerificationRedirectURL }); + await _futureAuthenticateWithPopup(SignUp.clerk, { + popup, + externalVerificationRedirectURL, + state: popupState, + }); // Pick up the modified SignUp resource await this.#resource.reload(); } else { diff --git a/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts b/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts index 628b665fc7c..b854b137728 100644 --- a/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts @@ -17,6 +17,14 @@ vi.mock('../../../utils/authenticateWithPopup', async () => { // Import the mocked function after mocking import { _futureAuthenticateWithPopup } from '../../../utils/authenticateWithPopup'; +const getFapiClient = () => ({ + buildUrl: ({ path, search }: { path: string; search?: Record }) => { + const url = new URL(`https://clerk.example.com/v1${path}`); + Object.entries(search ?? {}).forEach(([key, value]) => url.searchParams.set(key, value)); + return url; + }, +}); + // Mock the CaptchaChallenge module vi.mock('../../../utils/captcha/CaptchaChallenge', () => ({ CaptchaChallenge: vi.fn().mockImplementation(function () { @@ -2158,6 +2166,7 @@ describe('SignIn', () => { SignIn.clerk = { buildUrlWithAuth: vi.fn().mockReturnValue('https://example.com/sso-callback'), + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -2211,6 +2220,7 @@ describe('SignIn', () => { SignIn.clerk = { buildUrlWithAuth: vi.fn().mockReturnValue('https://example.com/sso-callback'), + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -2249,6 +2259,7 @@ describe('SignIn', () => { SignIn.clerk = { buildUrlWithAuth: vi.fn().mockReturnValue('https://example.com/sso-callback'), + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -2372,6 +2383,7 @@ describe('SignIn', () => { buildUrlWithAuth: mockBuildUrlWithAuth, buildUrl: vi.fn().mockImplementation(path => 'https://example.com' + path), frontendApi: 'clerk.example.com', + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -2448,6 +2460,7 @@ describe('SignIn', () => { buildUrlWithAuth: mockBuildUrlWithAuth, buildUrl: vi.fn().mockImplementation(path => 'https://example.com' + path), frontendApi: 'clerk.example.com', + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -2528,6 +2541,7 @@ describe('SignIn', () => { buildUrlWithAuth: mockBuildUrlWithAuth, buildUrl: vi.fn().mockImplementation(path => 'https://example.com' + path), frontendApi: 'clerk.example.com', + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -2577,6 +2591,7 @@ describe('SignIn', () => { expect.objectContaining({ popup: mockPopup, externalVerificationRedirectURL: expect.any(URL), + state: expect.stringMatching(/^[0-9a-f]{64}$/), }), ); expect(mockPopup.location.href).toBe('https://sso.example.com/auth'); @@ -2585,8 +2600,8 @@ describe('SignIn', () => { expect(mockFetch).toHaveBeenCalledWith( expect.objectContaining({ body: expect.objectContaining({ - redirectUrl: expect.stringContaining('/popup-callback'), - actionCompleteRedirectUrl: expect.stringContaining('/popup-callback'), + redirectUrl: expect.stringContaining('/auth-popup-callback'), + actionCompleteRedirectUrl: expect.stringContaining('/auth-popup-callback'), }), }), ); diff --git a/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts b/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts index a639c6244d8..b7692e80c89 100644 --- a/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts @@ -16,6 +16,14 @@ vi.mock('../../../utils/authenticateWithPopup', async () => { // Import the mocked function after mocking import { _futureAuthenticateWithPopup } from '../../../utils/authenticateWithPopup'; + +const getFapiClient = () => ({ + buildUrl: ({ path, search }: { path: string; search?: Record }) => { + const url = new URL(`https://clerk.example.com/v1${path}`); + Object.entries(search ?? {}).forEach(([key, value]) => url.searchParams.set(key, value)); + return url; + }, +}); import { CaptchaChallenge } from '../../../utils/captcha/CaptchaChallenge'; // Mock the CaptchaChallenge module @@ -1009,6 +1017,7 @@ describe('SignUp', () => { buildUrlWithAuth: mockBuildUrlWithAuth, buildUrl: vi.fn().mockImplementation(path => 'https://example.com' + path), frontendApi: 'clerk.example.com', + getFapiClient, __internal_environment: { displayConfig: { captchaOauthBypass: [], @@ -1031,9 +1040,10 @@ describe('SignUp', () => { }); BaseResource._fetch = mockFetch; - vi.mocked(_futureAuthenticateWithPopup).mockImplementation(async (_clerk, params) => { + vi.mocked(_futureAuthenticateWithPopup).mockImplementation((_clerk, params) => { // Simulate the actual behavior of setting popup href params.popup.location.href = params.externalVerificationRedirectURL.toString(); + return Promise.resolve(); }); const signUp = new SignUp(); @@ -1050,6 +1060,7 @@ describe('SignUp', () => { expect.objectContaining({ popup: mockPopup, externalVerificationRedirectURL: expect.any(URL), + state: expect.stringMatching(/^[0-9a-f]{64}$/), }), ); expect(mockPopup.location.href).toBe('https://sso.example.com/auth'); @@ -1058,8 +1069,8 @@ describe('SignUp', () => { expect(mockFetch).toHaveBeenCalledWith( expect.objectContaining({ body: expect.objectContaining({ - redirectUrl: expect.stringContaining('/popup-callback'), - actionCompleteRedirectUrl: expect.stringContaining('/popup-callback'), + redirectUrl: expect.stringContaining('/auth-popup-callback'), + actionCompleteRedirectUrl: expect.stringContaining('/auth-popup-callback'), }), }), ); diff --git a/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts b/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts new file mode 100644 index 00000000000..029a5ac03f8 --- /dev/null +++ b/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts @@ -0,0 +1,141 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import type { Clerk } from '../../core/clerk'; +import { createFapiClient } from '../../core/fapiClient'; +import { _authenticateWithPopup, _futureAuthenticateWithPopup, wrapWithPopupRoutes } from '../authenticateWithPopup'; + +const createClerk = (proxyUrl?: string) => { + const fapiClient = createFapiClient({ + frontendApi: 'clerk.example.com', + proxyUrl, + instanceType: 'production', + getSessionId: () => undefined, + }); + + return { + client: {}, + buildUrlWithAuth: vi.fn((url: string) => url), + getFapiClient: () => fapiClient, + } as unknown as Clerk; +}; + +afterEach(() => { + vi.restoreAllMocks(); +}); + +describe('wrapWithPopupRoutes', () => { + it.each([ + [undefined, 'https://clerk.example.com/v1/auth-popup-callback'], + ['https://app.example.com/__clerk', 'https://app.example.com/__clerk/v1/auth-popup-callback'], + ])('uses the effective FAPI URL when proxyUrl is %s', (proxyUrl, expectedCallbackUrl) => { + const routes = wrapWithPopupRoutes(createClerk(proxyUrl), { + redirectCallbackUrl: 'https://app.example.com/sso-callback', + redirectUrl: 'https://app.example.com/dashboard', + }); + + const completeUrl = new URL(routes.redirectUrl); + const callbackUrl = new URL(routes.redirectCallbackUrl); + + expect(`${completeUrl.origin}${completeUrl.pathname}`).toBe(expectedCallbackUrl); + expect(`${callbackUrl.origin}${callbackUrl.pathname}`).toBe(expectedCallbackUrl); + expect(completeUrl.searchParams.get('state')).toBe(routes.state); + expect(callbackUrl.searchParams.get('state')).toBe(routes.state); + expect(routes.state).toMatch(/^[a-f0-9]{64}$/); + expect(callbackUrl.searchParams.get('return_url')).toContain('https://app.example.com/sso-callback'); + }); +}); + +describe('_authenticateWithPopup', () => { + it('only accepts a message from the FAPI origin, expected popup, and generated state', async () => { + const clerk = createClerk(); + const popup = { location: { href: '' } } as unknown as Window; + const navigate = vi.fn(); + const authenticateMethod = vi.fn().mockResolvedValue(undefined); + Object.assign(clerk, { navigate }); + + await _authenticateWithPopup( + clerk, + 'signIn', + authenticateMethod, + { + popup, + strategy: 'oauth_google', + redirectUrl: 'https://app.example.com/sso-callback', + redirectUrlComplete: 'https://app.example.com/dashboard', + }, + vi.fn(), + ); + + const callbackUrl = new URL(authenticateMethod.mock.calls[0][0].redirectUrl); + const state = callbackUrl.searchParams.get('state'); + const returnUrl = callbackUrl.searchParams.get('return_url'); + + window.dispatchEvent( + new MessageEvent('message', { + data: { return_url: returnUrl, state }, + origin: 'https://accounts.example.com', + source: popup, + }), + ); + expect(navigate).not.toHaveBeenCalled(); + + window.dispatchEvent( + new MessageEvent('message', { + data: { return_url: returnUrl, state }, + origin: 'https://clerk.example.com', + source: popup, + }), + ); + expect(navigate).toHaveBeenCalledWith(returnUrl); + }); +}); + +describe('_futureAuthenticateWithPopup', () => { + it('only accepts a message from the FAPI origin, expected popup, and expected state', async () => { + const clerk = createClerk('https://app.example.com/__clerk'); + const popup = { location: { href: '' } } as unknown as Window; + const otherPopup = { location: { href: '' } } as unknown as Window; + const removeEventListener = vi.spyOn(window, 'removeEventListener'); + const authentication = _futureAuthenticateWithPopup(clerk, { + popup, + externalVerificationRedirectURL: new URL('https://oauth.example.com/authorize'), + state: 'expected_state', + }); + + window.dispatchEvent( + new MessageEvent('message', { + data: { session: 'sess_123', state: 'expected_state' }, + origin: 'https://accounts.example.com', + source: popup, + }), + ); + window.dispatchEvent( + new MessageEvent('message', { + data: { session: 'sess_123', state: 'expected_state' }, + origin: 'https://app.example.com', + source: otherPopup, + }), + ); + window.dispatchEvent( + new MessageEvent('message', { + data: { session: 'sess_123', state: 'wrong_state' }, + origin: 'https://app.example.com', + source: popup, + }), + ); + + expect(removeEventListener).not.toHaveBeenCalled(); + + window.dispatchEvent( + new MessageEvent('message', { + data: { session: 'sess_123', state: 'expected_state' }, + origin: 'https://app.example.com', + source: popup, + }), + ); + + await expect(authentication).resolves.toBeUndefined(); + expect(popup.location.href).toBe('https://oauth.example.com/authorize'); + expect(removeEventListener).toHaveBeenCalledWith('message', expect.any(Function)); + }); +}); diff --git a/packages/clerk-js/src/utils/authenticateWithPopup.ts b/packages/clerk-js/src/utils/authenticateWithPopup.ts index 9f107868f13..8dfb4fe7534 100644 --- a/packages/clerk-js/src/utils/authenticateWithPopup.ts +++ b/packages/clerk-js/src/utils/authenticateWithPopup.ts @@ -1,8 +1,28 @@ -import { buildAccountsBaseUrl } from '@clerk/shared/buildAccountsBaseUrl'; import type { AuthenticateWithPopupParams, AuthenticateWithRedirectParams } from '@clerk/shared/types'; import type { Clerk } from '../core/clerk'; +const popupCallbackPath = '/auth-popup-callback'; + +const createPopupState = () => + Array.from(crypto.getRandomValues(new Uint8Array(32)), byte => byte.toString(16).padStart(2, '0')).join(''); + +const buildPopupCallbackUrls = (client: Clerk, state: string, returnUrl: string) => { + const callbackUrl = client.getFapiClient().buildUrl({ + path: popupCallbackPath, + search: { state }, + }); + const popupRedirectUrlComplete = client.buildUrlWithAuth(callbackUrl.toString()); + const popupRedirectUrl = new URL(popupRedirectUrlComplete); + popupRedirectUrl.searchParams.set('return_url', returnUrl); + + return { + callbackOrigin: callbackUrl.origin, + popupRedirectUrl: popupRedirectUrl.toString(), + popupRedirectUrlComplete, + }; +}; + export async function _authenticateWithPopup( client: Clerk, reloadResource: 'signIn' | 'signUp', @@ -19,8 +39,6 @@ export async function _authenticateWithPopup( return; } - const accountPortalHost = buildAccountsBaseUrl(client.frontendApi); - const { redirectUrl } = params; // We set the force_redirect_url query parameter to ensure that the user is redirected to the correct page even @@ -29,16 +47,16 @@ export async function _authenticateWithPopup( r.searchParams.set('sign_in_force_redirect_url', params.redirectUrlComplete); r.searchParams.set('sign_up_force_redirect_url', params.redirectUrlComplete); r.searchParams.set('intent', reloadResource); - // All URLs are decorated with the dev browser token in development mode since we're moving between AP and the app. const redirectUrlWithForceRedirectUrl = client.buildUrlWithAuth(r.toString()); - - const popupRedirectUrlComplete = client.buildUrlWithAuth(`${accountPortalHost}/popup-callback`); - const popupRedirectUrl = client.buildUrlWithAuth( - `${accountPortalHost}/popup-callback?return_url=${encodeURIComponent(redirectUrlWithForceRedirectUrl)}`, + const state = createPopupState(); + const { callbackOrigin, popupRedirectUrl, popupRedirectUrlComplete } = buildPopupCallbackUrls( + client, + state, + redirectUrlWithForceRedirectUrl, ); const messageHandler = async (event: MessageEvent) => { - if (event.origin !== accountPortalHost) { + if (event.origin !== callbackOrigin || event.source !== params.popup || event.data?.state !== state) { return; } @@ -81,9 +99,7 @@ export async function _authenticateWithPopup( } /** - * Creates new redirect and callback URLs that point to the `/popup-callback` route on Account Portal. These URLs will - * be used by FAPI to redirect after the OAuth flow completes, and will result in a message being sent to the parent - * window. + * Creates new redirect and callback URLs that point to FAPI's popup callback route. */ export function wrapWithPopupRoutes( client: Clerk, @@ -100,28 +116,26 @@ export function wrapWithPopupRoutes( */ redirectUrl: string; }, -): { redirectCallbackUrl: string; redirectUrl: string } { - const accountPortalHost = buildAccountsBaseUrl(client.frontendApi); - +): { redirectCallbackUrl: string; redirectUrl: string; state: string } { // We set the force_redirect_url query parameter to ensure that the user is redirected to the correct page even // in situations like a modal transfer flow. const r = new URL(redirectCallbackUrl); r.searchParams.set('sign_in_force_redirect_url', redirectUrl); r.searchParams.set('sign_up_force_redirect_url', redirectUrl); - // All URLs are decorated with the dev browser token in development mode since we're moving between AP and the app. const redirectUrlWithForceRedirectUrl = client.buildUrlWithAuth(r.toString()); - - const popupRedirectUrlComplete = client.buildUrlWithAuth(`${accountPortalHost}/popup-callback`); - const popupRedirectUrl = client.buildUrlWithAuth( - `${accountPortalHost}/popup-callback?return_url=${encodeURIComponent(redirectUrlWithForceRedirectUrl)}`, + const state = createPopupState(); + const { popupRedirectUrl, popupRedirectUrlComplete } = buildPopupCallbackUrls( + client, + state, + redirectUrlWithForceRedirectUrl, ); - return { redirectCallbackUrl: popupRedirectUrl, redirectUrl: popupRedirectUrlComplete }; + return { redirectCallbackUrl: popupRedirectUrl, redirectUrl: popupRedirectUrlComplete, state }; } export function _futureAuthenticateWithPopup( client: Clerk, - params: { popup: { location: { href: string } }; externalVerificationRedirectURL: URL }, + params: { popup: Window; externalVerificationRedirectURL: URL; state: string }, ): Promise { return new Promise((resolve, reject) => { if (!client.client || !params.popup) { @@ -129,8 +143,9 @@ export function _futureAuthenticateWithPopup( return; } - const messageHandler = async (event: MessageEvent) => { - if (event.origin !== buildAccountsBaseUrl(client.frontendApi)) { + const callbackOrigin = client.getFapiClient().buildUrl({ path: popupCallbackPath }).origin; + const messageHandler = (event: MessageEvent) => { + if (event.origin !== callbackOrigin || event.source !== params.popup || event.data?.state !== params.state) { return; } From 0b0c9fb3f74a91f3c21ed9e84793116c6f0bfdfc Mon Sep 17 00:00:00 2001 From: brkalow Date: Wed, 15 Jul 2026 23:18:31 -0500 Subject: [PATCH 2/2] Rename FAPI popup callback route --- packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts | 4 ++-- packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts | 4 ++-- .../src/utils/__tests__/authenticateWithPopup.test.ts | 4 ++-- packages/clerk-js/src/utils/authenticateWithPopup.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts b/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts index b854b137728..26df8d0f9a8 100644 --- a/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts @@ -2600,8 +2600,8 @@ describe('SignIn', () => { expect(mockFetch).toHaveBeenCalledWith( expect.objectContaining({ body: expect.objectContaining({ - redirectUrl: expect.stringContaining('/auth-popup-callback'), - actionCompleteRedirectUrl: expect.stringContaining('/auth-popup-callback'), + redirectUrl: expect.stringContaining('/popup_auth_callback'), + actionCompleteRedirectUrl: expect.stringContaining('/popup_auth_callback'), }), }), ); diff --git a/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts b/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts index b7692e80c89..b3f8c51bfb9 100644 --- a/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts @@ -1069,8 +1069,8 @@ describe('SignUp', () => { expect(mockFetch).toHaveBeenCalledWith( expect.objectContaining({ body: expect.objectContaining({ - redirectUrl: expect.stringContaining('/auth-popup-callback'), - actionCompleteRedirectUrl: expect.stringContaining('/auth-popup-callback'), + redirectUrl: expect.stringContaining('/popup_auth_callback'), + actionCompleteRedirectUrl: expect.stringContaining('/popup_auth_callback'), }), }), ); diff --git a/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts b/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts index 029a5ac03f8..d01cba18e6a 100644 --- a/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts +++ b/packages/clerk-js/src/utils/__tests__/authenticateWithPopup.test.ts @@ -25,8 +25,8 @@ afterEach(() => { describe('wrapWithPopupRoutes', () => { it.each([ - [undefined, 'https://clerk.example.com/v1/auth-popup-callback'], - ['https://app.example.com/__clerk', 'https://app.example.com/__clerk/v1/auth-popup-callback'], + [undefined, 'https://clerk.example.com/v1/popup_auth_callback'], + ['https://app.example.com/__clerk', 'https://app.example.com/__clerk/v1/popup_auth_callback'], ])('uses the effective FAPI URL when proxyUrl is %s', (proxyUrl, expectedCallbackUrl) => { const routes = wrapWithPopupRoutes(createClerk(proxyUrl), { redirectCallbackUrl: 'https://app.example.com/sso-callback', diff --git a/packages/clerk-js/src/utils/authenticateWithPopup.ts b/packages/clerk-js/src/utils/authenticateWithPopup.ts index 8dfb4fe7534..9bbf59a3fce 100644 --- a/packages/clerk-js/src/utils/authenticateWithPopup.ts +++ b/packages/clerk-js/src/utils/authenticateWithPopup.ts @@ -2,7 +2,7 @@ import type { AuthenticateWithPopupParams, AuthenticateWithRedirectParams } from import type { Clerk } from '../core/clerk'; -const popupCallbackPath = '/auth-popup-callback'; +const popupCallbackPath = '/popup_auth_callback'; const createPopupState = () => Array.from(crypto.getRandomValues(new Uint8Array(32)), byte => byte.toString(16).padStart(2, '0')).join('');