Skip to content

Commit c0ba3bc

Browse files
authored
fix(clerk-js): send oidcPrompt when creating an OAuth sign-in (#10125)
1 parent 8361768 commit c0ba3bc

3 files changed

Lines changed: 88 additions & 3 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fix `oidcPrompt` being ignored for OAuth strategies in `signIn.sso()` and `signIn.authenticateWithRedirect()`. The value is now sent when the sign-in is created, so a prompt such as `select_account` reaches the OAuth provider.

‎packages/clerk-js/src/core/resources/SignIn.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ export class SignIn extends BaseResource implements SignInResource {
445445
identifier,
446446
redirectUrl,
447447
actionCompleteRedirectUrl,
448+
oidcPrompt,
448449
});
449450

450451
if (isChallengePending()) {
@@ -1355,6 +1356,7 @@ class SignInFuture implements SignInFutureResource {
13551356
strategy,
13561357
...routes,
13571358
identifier,
1359+
oidcPrompt,
13581360
},
13591361
{ resolveProtectCheck: false },
13601362
);

‎packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts‎

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ vi.mock('../../../utils/captcha/CaptchaChallenge', () => ({
3333
}),
3434
}));
3535

36+
const toFormFields = (body: Record<string, unknown>) =>
37+
Object.fromEntries(new URLSearchParams(stringifyQueryParams(body, { keyEncoder: camelToSnake })));
38+
3639
describe('SignIn', () => {
3740
beforeEach(() => {
3841
vi.stubGlobal('Intl', undefined);
@@ -332,6 +335,49 @@ describe('SignIn', () => {
332335
});
333336
});
334337

338+
describe('authenticateWithRedirect with oidcPrompt', () => {
339+
const originalFetch = BaseResource._fetch;
340+
341+
afterEach(() => {
342+
BaseResource._fetch = originalFetch;
343+
vi.clearAllMocks();
344+
SignIn.clerk = {} as any;
345+
});
346+
347+
it('sends oidc_prompt in the request that creates an OAuth sign-in', async () => {
348+
SignIn.clerk = {
349+
buildUrlWithAuth: vi.fn(u => u),
350+
__internal_windowNavigate: vi.fn(),
351+
__internal_environment: { displayConfig: { captchaOauthBypass: [] } },
352+
} as any;
353+
const mockFetch = vi.fn().mockResolvedValue({
354+
client: null,
355+
response: {
356+
id: 'signin_123',
357+
first_factor_verification: {
358+
status: 'unverified',
359+
external_verification_redirect_url: 'https://accounts.google.example/auth',
360+
},
361+
},
362+
});
363+
BaseResource._fetch = mockFetch;
364+
365+
await new SignIn().authenticateWithRedirect({
366+
strategy: 'oauth_google',
367+
redirectUrl: '/sso-callback',
368+
redirectUrlComplete: '/',
369+
oidcPrompt: 'select_account',
370+
});
371+
372+
expect(mockFetch).toHaveBeenCalledTimes(1);
373+
expect(mockFetch.mock.calls[0][0]).toMatchObject({ method: 'POST', path: '/client/sign_ins' });
374+
expect(toFormFields(mockFetch.mock.calls[0][0].body)).toMatchObject({
375+
strategy: 'oauth_google',
376+
oidc_prompt: 'select_account',
377+
});
378+
});
379+
});
380+
335381
describe('authenticateWithRedirect with a pending challenge', () => {
336382
const originalFetch = BaseResource._fetch;
337383

@@ -769,9 +815,6 @@ describe('SignIn', () => {
769815
vi.unstubAllGlobals();
770816
});
771817

772-
const toFormFields = (body: Record<string, unknown>) =>
773-
Object.fromEntries(new URLSearchParams(stringifyQueryParams(body, { keyEncoder: camelToSnake })));
774-
775818
it('creates a sign-in with a trusted device id', async () => {
776819
vi.stubGlobal('navigator', { language: '' });
777820
const mockFetch = vi.fn().mockResolvedValue({
@@ -2808,6 +2851,41 @@ describe('SignIn', () => {
28082851
vi.unstubAllGlobals();
28092852
});
28102853

2854+
it('sends oidc_prompt in the request that creates an OAuth sign-in', async () => {
2855+
vi.stubGlobal('window', { location: { origin: 'https://example.com' } });
2856+
SignIn.clerk = {
2857+
buildUrlWithAuth: vi.fn().mockReturnValue('https://example.com/sso-callback'),
2858+
__internal_windowNavigate: vi.fn(),
2859+
__internal_environment: { displayConfig: { captchaOauthBypass: [] } },
2860+
} as any;
2861+
const mockFetch = vi.fn().mockResolvedValue({
2862+
client: null,
2863+
response: {
2864+
id: 'signin_123',
2865+
first_factor_verification: {
2866+
status: 'unverified',
2867+
external_verification_redirect_url: 'https://accounts.google.example/auth',
2868+
},
2869+
},
2870+
});
2871+
BaseResource._fetch = mockFetch;
2872+
2873+
const { error } = await new SignIn().__internal_future.sso({
2874+
strategy: 'oauth_google',
2875+
redirectUrl: '/dashboard',
2876+
redirectCallbackUrl: '/sso-callback',
2877+
oidcPrompt: 'select_account',
2878+
});
2879+
2880+
expect(error).toBeNull();
2881+
expect(mockFetch).toHaveBeenCalledTimes(1);
2882+
expect(mockFetch.mock.calls[0][0]).toMatchObject({ method: 'POST', path: '/client/sign_ins' });
2883+
expect(toFormFields(mockFetch.mock.calls[0][0].body)).toMatchObject({
2884+
strategy: 'oauth_google',
2885+
oidc_prompt: 'select_account',
2886+
});
2887+
});
2888+
28112889
it('creates signIn with enterprise_sso strategy and prepares first factor', async () => {
28122890
vi.stubGlobal('window', { location: { origin: 'https://example.com' } });
28132891

0 commit comments

Comments
 (0)