From 964e2a6438fd792943093002957644a64a932b57 Mon Sep 17 00:00:00 2001 From: Jacek Date: Wed, 22 Apr 2026 22:18:13 -0500 Subject: [PATCH 1/3] test(integration): add failing e2e for openSignIn honoring ClerkProvider.signInUrl --- .changeset/test-opensignin-signinurl.md | 2 ++ integration/tests/oauth-flows.test.ts | 31 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .changeset/test-opensignin-signinurl.md diff --git a/.changeset/test-opensignin-signinurl.md b/.changeset/test-opensignin-signinurl.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/test-opensignin-signinurl.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/integration/tests/oauth-flows.test.ts b/integration/tests/oauth-flows.test.ts index 1ab1ea043ff..b1eb0e6f6c5 100644 --- a/integration/tests/oauth-flows.test.ts +++ b/integration/tests/oauth-flows.test.ts @@ -1,5 +1,5 @@ import { createClerkClient } from '@clerk/backend'; -import { test } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { appConfigs } from '../presets'; import { instanceKeys } from '../presets/envs'; @@ -91,6 +91,35 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('oauth flo await u.po.expect.toBeSignedIn(); }); + test('openSignIn OAuth uses ClerkProvider.signInUrl for sso-callback', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + + await u.page.goToRelative('/buttons'); + await u.page.waitForClerkJsLoaded(); + await u.po.expect.toBeSignedOut(); + + await u.page.evaluate(() => { + (window as any).Clerk.openSignIn({ forceRedirectUrl: '/protected' }); + }); + await u.po.signIn.waitForModal(); + + const signInPostPromise = page.waitForRequest( + req => req.method() === 'POST' && /\/v1\/client\/sign_ins(\?|$)/.test(req.url()), + ); + + await u.page.getByRole('button', { name: 'E2E OAuth Provider' }).click(); + + const signInPost = await signInPostPromise; + const body = new URLSearchParams(signInPost.postData() || ''); + const redirectUrl = body.get('redirect_url'); + expect(redirectUrl).toBeTruthy(); + + // The sso-callback should be served from the app's origin (derived from ClerkProvider.signInUrl), + // not from the accounts portal / displayConfig.signInUrl origin. + const appOrigin = new URL(app.serverUrl).origin; + expect(new URL(redirectUrl!).origin).toBe(appOrigin); + }); + test('sign up modal', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); // The SignUpModal will only redirect to its provided forceRedirectUrl if the user is signing up; it will not From ede31bcd079f3379632674d8be2dfd664152a447 Mon Sep 17 00:00:00 2001 From: Jacek Date: Wed, 22 Apr 2026 22:36:26 -0500 Subject: [PATCH 2/3] test(e2e): assert sso-callback path is /sign-in to pin ClerkProvider.signInUrl --- integration/tests/oauth-flows.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/integration/tests/oauth-flows.test.ts b/integration/tests/oauth-flows.test.ts index b1eb0e6f6c5..b381e084605 100644 --- a/integration/tests/oauth-flows.test.ts +++ b/integration/tests/oauth-flows.test.ts @@ -114,10 +114,14 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('oauth flo const redirectUrl = body.get('redirect_url'); expect(redirectUrl).toBeTruthy(); - // The sso-callback should be served from the app's origin (derived from ClerkProvider.signInUrl), - // not from the accounts portal / displayConfig.signInUrl origin. + // The sso-callback base must come from ClerkProvider.signInUrl (CLERK_SIGN_IN_URL=/sign-in in this fixture). + // Asserting origin alone would also pass for a blanket window.location.href style fix; asserting the + // pathname is /sign-in pins the redirect to ClerkProvider.signInUrl rather than displayConfig.signInUrl + // (accounts portal) or the current page URL. + const parsed = new URL(redirectUrl!); const appOrigin = new URL(app.serverUrl).origin; - expect(new URL(redirectUrl!).origin).toBe(appOrigin); + expect(parsed.origin).toBe(appOrigin); + expect(parsed.pathname).toBe('/sign-in'); }); test('sign up modal', async ({ page, context }) => { From 7e50f290381e36257cc038d33cbecfb23005cf79 Mon Sep 17 00:00:00 2001 From: Jacek Date: Wed, 22 Apr 2026 23:10:00 -0500 Subject: [PATCH 3/3] test(e2e): assert sso-callback hash fragment on openSignIn redirect --- integration/tests/oauth-flows.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration/tests/oauth-flows.test.ts b/integration/tests/oauth-flows.test.ts index b381e084605..e311c1697f1 100644 --- a/integration/tests/oauth-flows.test.ts +++ b/integration/tests/oauth-flows.test.ts @@ -117,11 +117,14 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('oauth flo // The sso-callback base must come from ClerkProvider.signInUrl (CLERK_SIGN_IN_URL=/sign-in in this fixture). // Asserting origin alone would also pass for a blanket window.location.href style fix; asserting the // pathname is /sign-in pins the redirect to ClerkProvider.signInUrl rather than displayConfig.signInUrl - // (accounts portal) or the current page URL. + // (accounts portal) or the current page URL. The hash assertion guarantees the callback actually targets + // the sso-callback route — without it, a regression that drops the #/sso-callback fragment would still + // satisfy origin/pathname while breaking the OAuth return path at runtime. const parsed = new URL(redirectUrl!); const appOrigin = new URL(app.serverUrl).origin; expect(parsed.origin).toBe(appOrigin); expect(parsed.pathname).toBe('/sign-in'); + expect(parsed.hash).toMatch(/^#\/sso-callback/); }); test('sign up modal', async ({ page, context }) => {