From 8b80b249623e4d4a98e93e73d80b0f9a75832491 Mon Sep 17 00:00:00 2001 From: Mike Wickett Date: Mon, 26 Oct 2026 17:46:11 -0400 Subject: [PATCH 1/4] test(e2e): stabilize the SSO sign-up and multi-session fixtures The SSO sign-up test now also sets up a testing token for the E2E OAuth Provider instance, whose sign-in runs on the provider's own frontend API. The transitive-state test waits for the second user to become the current user before navigating to the sign-out paths. --- .changeset/e2e-stabilize-fixtures.md | 2 ++ integration/tests/session-tasks-sign-up.test.ts | 12 ++++++++++++ integration/tests/transitive-state.test.ts | 1 + 3 files changed, 15 insertions(+) create mode 100644 .changeset/e2e-stabilize-fixtures.md diff --git a/.changeset/e2e-stabilize-fixtures.md b/.changeset/e2e-stabilize-fixtures.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/e2e-stabilize-fixtures.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/integration/tests/session-tasks-sign-up.test.ts b/integration/tests/session-tasks-sign-up.test.ts index 1753a9efeed..3432235aa92 100644 --- a/integration/tests/session-tasks-sign-up.test.ts +++ b/integration/tests/session-tasks-sign-up.test.ts @@ -1,6 +1,7 @@ import { createClerkClient } from '@clerk/backend'; import { expect, test } from '@playwright/test'; +import { environmentConfig } from '../models/environment'; import { appConfigs } from '../presets'; import { resolveInstanceKeys } from '../presets/envs'; import type { FakeUser } from '../testUtils'; @@ -79,6 +80,17 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })( test('with sso, navigate to task on after sign-up', async ({ page, context }) => { const u = await createTestUtils({ app, page, context }); + const providerKeys = await resolveInstanceKeys('oauth-provider'); + await createTestUtils({ + app: { + ...app, + env: environmentConfig() + .setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', providerKeys.pk) + .setEnvVariable('private', 'CLERK_SECRET_KEY', providerKeys.sk), + }, + page, + context, + }); await u.po.signUp.goTo(); await u.page.getByRole('button', { name: 'E2E OAuth Provider' }).click(); diff --git a/integration/tests/transitive-state.test.ts b/integration/tests/transitive-state.test.ts index 17d6566cd43..1026d593476 100644 --- a/integration/tests/transitive-state.test.ts +++ b/integration/tests/transitive-state.test.ts @@ -227,6 +227,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('transitiv await u.po.signIn.continue(); await u.po.signIn.setPassword(userB.password); await u.po.signIn.continue(); + await u.page.waitForFunction(userId => window.Clerk?.user?.id === userId, userBId); const pathA = '/transitive-state/sign-out'; const pathB = '/transitive-state/sign-out/sign-in'; From 608179de58df5999fa87548c0d93072974ef8a77 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 26 Oct 2026 15:13:55 -0700 Subject: [PATCH 2/4] test(e2e): cover the OAuth callback when the provider skips consent --- integration/tests/testing-token.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/integration/tests/testing-token.test.ts b/integration/tests/testing-token.test.ts index c230fada14c..2be97a838e1 100644 --- a/integration/tests/testing-token.test.ts +++ b/integration/tests/testing-token.test.ts @@ -13,6 +13,7 @@ test.describe('OAuth testing tokens @nextjs', () => { let appOrigin: string; let consentPosts: string[]; let consentTokens: (string | null)[]; + let authorizeTokens: (string | null)[]; test.beforeAll(async () => { app = createServer((request, response) => { @@ -37,6 +38,10 @@ test.describe('OAuth testing tokens @nextjs', () => { }); response.end('Consent accepted'); }); + } else if (url.pathname === '/oauth/authorize/continue') { + authorizeTokens.push(url.searchParams.get('__clerk_testing_token')); + response.writeHead(303, { location: url.searchParams.get('callback') || '/' }); + response.end('Authorized'); } else { response.end(JSON.stringify({ url: request.url, method: request.method })); } @@ -49,6 +54,7 @@ test.describe('OAuth testing tokens @nextjs', () => { test.beforeEach(async ({ context }) => { consentPosts = []; consentTokens = []; + authorizeTokens = []; await setupTestingTokens({ context, frontendApiOrigin: appOrigin, token: 'app-testing-token' }); }); @@ -95,6 +101,21 @@ test.describe('OAuth testing tokens @nextjs', () => { expect((await context.cookies(providerOrigin)).map(cookie => cookie.name).sort()).toEqual(['consent', 'provider']); }); + test('delivers the token on the callback when the provider skips the consent screen', async ({ page }) => { + const callback = new URL('/v1/oauth_callback?code=auth-code&state=a%2Bb%2F%3D', appOrigin); + const authorize = new URL('/oauth/authorize/continue', providerOrigin); + authorize.searchParams.set('callback', callback.toString()); + await page.goto(authorize.toString()); + + expect(authorizeTokens).toEqual([null]); + const received = JSON.parse((await page.locator('body').textContent()) || '{}'); + expect(Object.fromEntries(new URL(received.url, appOrigin).searchParams)).toEqual({ + code: 'auth-code', + state: 'a+b/=', + __clerk_testing_token: 'app-testing-token', + }); + }); + for (const [name, getCallback] of [ ['another origin', () => new URL('/v1/oauth_callback?code=auth-code', providerOrigin)], ['another path', () => new URL('/sso-callback?code=auth-code', appOrigin)], From e96ba90beeff799df519af3758743c4879625713 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 26 Oct 2026 15:14:31 -0700 Subject: [PATCH 3/4] fix(e2e): stamp the testing token on the OAuth callback when consent is skipped --- .../oauth-authorize-continue-testing-token.md | 2 ++ integration/testUtils/setupTestingTokens.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .changeset/oauth-authorize-continue-testing-token.md diff --git a/.changeset/oauth-authorize-continue-testing-token.md b/.changeset/oauth-authorize-continue-testing-token.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/oauth-authorize-continue-testing-token.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/integration/testUtils/setupTestingTokens.ts b/integration/testUtils/setupTestingTokens.ts index e70a50d9810..b1ac8738ad6 100644 --- a/integration/testUtils/setupTestingTokens.ts +++ b/integration/testUtils/setupTestingTokens.ts @@ -22,15 +22,21 @@ export const setupTestingTokens = async ({ await context.route( url => - (tokens.has(url.origin) && url.pathname.startsWith('/v1/')) || url.pathname.startsWith('/v1/me/oauth/consent/'), + (tokens.has(url.origin) && url.pathname.startsWith('/v1/')) || + url.pathname.startsWith('/v1/me/oauth/consent/') || + url.pathname === '/oauth/authorize/continue', async route => { - const requestUrl = new URL(route.request().url()); - const requestToken = tokens.get(requestUrl.origin); + const request = route.request(); + const requestUrl = new URL(request.url()); + const requestToken = requestUrl.pathname.startsWith('/v1/') ? tokens.get(requestUrl.origin) : undefined; if (requestToken) { requestUrl.searchParams.set('__clerk_testing_token', requestToken); } - if (route.request().method() !== 'POST' || !requestUrl.pathname.startsWith('/v1/me/oauth/consent/')) { + const redirectsToCallback = + (request.method() === 'POST' && requestUrl.pathname.startsWith('/v1/me/oauth/consent/')) || + (request.method() === 'GET' && requestUrl.pathname === '/oauth/authorize/continue'); + if (!redirectsToCallback) { if (!requestToken) { return route.fallback(); } From 09943dccf1a4dc522344c6035ec0b31b9cde9ee6 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 26 Oct 2026 15:22:05 -0700 Subject: [PATCH 4/4] test(e2e): assert the SSO sign-up callback carries the testing token --- integration/tests/session-tasks-sign-up.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/integration/tests/session-tasks-sign-up.test.ts b/integration/tests/session-tasks-sign-up.test.ts index 3432235aa92..3511ceedebb 100644 --- a/integration/tests/session-tasks-sign-up.test.ts +++ b/integration/tests/session-tasks-sign-up.test.ts @@ -101,7 +101,11 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })( await u.po.signUp.waitForMounted(); await u.po.signUp.setEmailAddress(fakeUserForOAuth.email); await u.po.signUp.continue(); - await u.po.signUp.enterTestOtpCode(); + const [callback] = await Promise.all([ + page.waitForRequest(request => new URL(request.url()).pathname === '/v1/oauth_callback'), + u.po.signUp.enterTestOtpCode(), + ]); + expect(new URL(callback.url()).searchParams.has('__clerk_testing_token')).toBe(true); // Resolves task await u.po.signUp.waitForMounted();