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..e311c1697f1 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,42 @@ 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 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. 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 }) => { const u = createTestUtils({ app, page, context }); // The SignUpModal will only redirect to its provided forceRedirectUrl if the user is signing up; it will not