From 5ef872cc8813367785d49a87fbbf300b9391081a Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Mon, 3 Nov 2025 18:24:40 -0500 Subject: [PATCH 1/2] fix(clerk-js): Handle SAML strategies --- .../last-authentication-strategy.test.ts | 22 +++++++++++++++++++ .../components/SignIn/SignInSocialButtons.tsx | 2 +- .../src/ui/elements/SocialButtons.tsx | 8 ++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/integration/tests/last-authentication-strategy.test.ts b/integration/tests/last-authentication-strategy.test.ts index f4456f3e8bf..6ac3c3d36db 100644 --- a/integration/tests/last-authentication-strategy.test.ts +++ b/integration/tests/last-authentication-strategy.test.ts @@ -74,6 +74,28 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( await expect(socialButtonContainers.first().locator('.cl-button')).toHaveCount(3); }); + test('should show "Last used" badge when lastAuthenticationStrategy is oauth_google', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + await mockLastAuthenticationStrategyResponse(page, 'saml_google'); + + await u.po.signIn.goTo(); + await u.po.signIn.waitForMounted(); + + // Ensure "Last used" badge is present. + const lastUsedBadge = page.locator('.cl-lastAuthenticationStrategyBadge'); + await expect(lastUsedBadge).toBeVisible(); + await expect(lastUsedBadge).toHaveCount(1); + + const btn = page.getByRole('button', { name: 'Last used Sign in with Google' }); + await expect(btn).toBeVisible(); + + // Ensure the last used social button has been pulled to the first row. + const socialButtonContainers = u.page.locator('.cl-socialButtons'); + await expect(socialButtonContainers).toHaveCount(2); + await expect(socialButtonContainers.first().locator('.cl-button__google')).toHaveCount(1); + await expect(socialButtonContainers.last().locator('.cl-button')).toHaveCount(2); + }); + test('should show "Last used" badge when lastAuthenticationStrategy is oauth_google', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); await mockLastAuthenticationStrategyResponse(page, 'oauth_google'); diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx index ad745d83eba..52e7c5a39bf 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx @@ -33,7 +33,7 @@ export const SignInSocialButtons = React.memo((props: SignInSocialButtonsProps) return ( { if (shouldUsePopup) { diff --git a/packages/clerk-js/src/ui/elements/SocialButtons.tsx b/packages/clerk-js/src/ui/elements/SocialButtons.tsx index 940a5210a01..d628b5d31d6 100644 --- a/packages/clerk-js/src/ui/elements/SocialButtons.tsx +++ b/packages/clerk-js/src/ui/elements/SocialButtons.tsx @@ -87,7 +87,13 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => { return strategies.includes(strategy as TStrategy); }; - const lastAuthenticationStrategy = clientLastAuth && isValidStrategy(clientLastAuth) ? clientLastAuth : null; + // Convert SAML strategies to OAuth strategies for consistency when matching last used strategy. + const convertedClientLastAuth = clientLastAuth.startsWith('saml_') + ? clientLastAuth.replace('saml_', 'oauth_') + : clientLastAuth; + + const lastAuthenticationStrategy = + convertedClientLastAuth && isValidStrategy(convertedClientLastAuth) ? convertedClientLastAuth : null; const { strategyRows, lastAuthenticationStrategyPresent } = distributeStrategiesIntoRows( [...strategies], From 9d45d863f4f1ce9a7b06ec10acda678c379b3e7b Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Mon, 3 Nov 2025 18:34:39 -0500 Subject: [PATCH 2/2] chore: Minor updates & changeset --- .changeset/eleven-phones-say.md | 5 +++++ integration/tests/last-authentication-strategy.test.ts | 2 +- packages/clerk-js/src/ui/elements/SocialButtons.tsx | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/eleven-phones-say.md diff --git a/.changeset/eleven-phones-say.md b/.changeset/eleven-phones-say.md new file mode 100644 index 00000000000..82575ec5bdb --- /dev/null +++ b/.changeset/eleven-phones-say.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +fix: Appropriately handle last-used SAML strategies diff --git a/integration/tests/last-authentication-strategy.test.ts b/integration/tests/last-authentication-strategy.test.ts index 6ac3c3d36db..09449c0fde8 100644 --- a/integration/tests/last-authentication-strategy.test.ts +++ b/integration/tests/last-authentication-strategy.test.ts @@ -74,7 +74,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( await expect(socialButtonContainers.first().locator('.cl-button')).toHaveCount(3); }); - test('should show "Last used" badge when lastAuthenticationStrategy is oauth_google', async ({ page, context }) => { + test('should show "Last used" badge when lastAuthenticationStrategy is saml_google', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); await mockLastAuthenticationStrategyResponse(page, 'saml_google'); diff --git a/packages/clerk-js/src/ui/elements/SocialButtons.tsx b/packages/clerk-js/src/ui/elements/SocialButtons.tsx index d628b5d31d6..854acf6814b 100644 --- a/packages/clerk-js/src/ui/elements/SocialButtons.tsx +++ b/packages/clerk-js/src/ui/elements/SocialButtons.tsx @@ -88,7 +88,7 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => { }; // Convert SAML strategies to OAuth strategies for consistency when matching last used strategy. - const convertedClientLastAuth = clientLastAuth.startsWith('saml_') + const convertedClientLastAuth = clientLastAuth?.startsWith('saml_') ? clientLastAuth.replace('saml_', 'oauth_') : clientLastAuth;