feat(ui,shared,localizations): provider selection step for <__experimental_ConfigureSSO />#8503
feat(ui,shared,localizations): provider selection step for <__experimental_ConfigureSSO />#8503iagodahlem wants to merge 16 commits into
<__experimental_ConfigureSSO />#8503Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 62a608d The changes in this PR will be included in the next version bump. This PR includes changesets to release 20 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
7dc3f44 to
94f83bd
Compare
aec960d to
53bbef8
Compare
53bbef8 to
87dc071
Compare
<__experimental_ConfigureSSO />
| align='start' | ||
| justify='between' | ||
| sx={theme => ({ gap: theme.space.$4 })} | ||
| > | ||
| <Col sx={theme => ({ gap: theme.space.$1x5, minWidth: 0 })}> | ||
| <Col sx={theme => ({ gap: theme.space.$2, minWidth: 0 })}> |
There was a problem hiding this comment.
Updates the badge display on the header.
<__experimental_ConfigureSSO /><__experimental_ConfigureSSO />
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughThis PR implements the "Select Provider" step for the experimental ConfigureSSO wizard component. It adds support for selecting SAML providers (Okta Workforce and Custom SAML), with localized UI strings across 50 supported languages. The context layer is refactored to manage provider state, derive the wizard's entry step based on enterprise connection metadata, and provide an idempotent connection creation function. SelectProviderStep receives a complete implementation with provider selection cards, error handling, warnings about provider lock-in, and comprehensive test coverage. Supporting components are updated to integrate with the new state model. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/localizations/src/kk-KZ.ts`:
- Around line 185-198: The selectProviderStep localization object contains
English strings that must be translated to Kazakh; update the
selectProviderStep.title, subtitle, body.title, body.description,
saml.groupLabel, saml.okta, saml.customSaml, and warning in the kk-KZ.ts file to
Kazakh equivalents (or add an agreed temporary fallback translation) so the
locale no longer renders mixed-language UI; ensure you edit the
selectProviderStep object and preserve the same keys and structure.
In `@packages/ui/src/components/ConfigureSSO/__tests__/deriveInitialStep.test.ts`:
- Line 4: The test currently imports the type WizardStepId from
'../deriveInitialStep' but that type is actually exported from '../types';
update the import statement to import deriveInitialStep (function) from
'../deriveInitialStep' and WizardStepId (type) from '../types' so the file
imports "deriveInitialStep" and "WizardStepId" from their correct modules
(locate the current import line that references deriveInitialStep and change the
type import target to '../types').
In `@packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx`:
- Around line 59-76: The createConnectionFetcher function can call
createEnterpriseConnection multiple times before parent state updates because it
only checks the props enterpriseConnection; add an in-flight guard to make it
truly idempotent by introducing a local flag/ref (e.g., isCreatingRef) that is
checked at the start of createConnectionFetcher and set to true before calling
createEnterpriseConnection and cleared in finally, and also ensure you
short-circuit if createEnterpriseConnection returns an existing/duplicate
connection; keep the existing user.primaryEmailAddress and
session.lastActiveOrganizationId logic but use the guard around the
createEnterpriseConnection call (references: createConnectionFetcher,
enterpriseConnection, createEnterpriseConnection, user.primaryEmailAddress,
session.lastActiveOrganizationId).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: e524dd6f-7a5d-4904-9540-5bc12097a0c5
📒 Files selected for processing (60)
.changeset/configure-sso-select-provider-step.mdpackages/localizations/src/ar-SA.tspackages/localizations/src/be-BY.tspackages/localizations/src/bg-BG.tspackages/localizations/src/bn-IN.tspackages/localizations/src/ca-ES.tspackages/localizations/src/cs-CZ.tspackages/localizations/src/da-DK.tspackages/localizations/src/de-DE.tspackages/localizations/src/el-GR.tspackages/localizations/src/en-GB.tspackages/localizations/src/en-US.tspackages/localizations/src/es-CR.tspackages/localizations/src/es-ES.tspackages/localizations/src/es-MX.tspackages/localizations/src/es-UY.tspackages/localizations/src/fa-IR.tspackages/localizations/src/fi-FI.tspackages/localizations/src/fr-FR.tspackages/localizations/src/he-IL.tspackages/localizations/src/hi-IN.tspackages/localizations/src/hr-HR.tspackages/localizations/src/hu-HU.tspackages/localizations/src/id-ID.tspackages/localizations/src/is-IS.tspackages/localizations/src/it-IT.tspackages/localizations/src/ja-JP.tspackages/localizations/src/kk-KZ.tspackages/localizations/src/ko-KR.tspackages/localizations/src/mn-MN.tspackages/localizations/src/ms-MY.tspackages/localizations/src/nb-NO.tspackages/localizations/src/nl-BE.tspackages/localizations/src/nl-NL.tspackages/localizations/src/pl-PL.tspackages/localizations/src/pt-BR.tspackages/localizations/src/pt-PT.tspackages/localizations/src/ro-RO.tspackages/localizations/src/ru-RU.tspackages/localizations/src/sk-SK.tspackages/localizations/src/sr-RS.tspackages/localizations/src/sv-SE.tspackages/localizations/src/ta-IN.tspackages/localizations/src/te-IN.tspackages/localizations/src/th-TH.tspackages/localizations/src/tr-TR.tspackages/localizations/src/uk-UA.tspackages/localizations/src/vi-VN.tspackages/localizations/src/zh-CN.tspackages/localizations/src/zh-TW.tspackages/shared/src/types/localization.tspackages/ui/src/components/ConfigureSSO/ConfigureSSO.tsxpackages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsxpackages/ui/src/components/ConfigureSSO/__tests__/deriveInitialStep.test.tspackages/ui/src/components/ConfigureSSO/deriveInitialStep.tspackages/ui/src/components/ConfigureSSO/elements/Step.tsxpackages/ui/src/components/ConfigureSSO/steps/ConfigureStep.tsxpackages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsxpackages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsxpackages/ui/src/components/ConfigureSSO/types.ts
| selectProviderStep: { | ||
| title: 'Select provider', | ||
| subtitle: 'Select the provider you are going to setup SSO for.', | ||
| body: { | ||
| title: 'Select your identity provider', | ||
| description: "We'll guide you through the detailed setup process next.", | ||
| }, | ||
| saml: { | ||
| groupLabel: 'SAML', | ||
| okta: 'Okta Workforce', | ||
| customSaml: 'Custom SAML Provider', | ||
| }, | ||
| warning: 'Once a provider is selected you cannot change again until the configuration is over', | ||
| }, |
There was a problem hiding this comment.
kk-KZ Select Provider strings are still English.
Line 186 through Line 197 are not localized to Kazakh, so this locale will render mixed-language UI in the new SSO step.
Suggested temporary fallback until Kazakh copy is provided
selectProviderStep: {
- title: 'Select provider',
- subtitle: 'Select the provider you are going to setup SSO for.',
+ title: undefined,
+ subtitle: undefined,
body: {
- title: 'Select your identity provider',
- description: "We'll guide you through the detailed setup process next.",
+ title: undefined,
+ description: undefined,
},
saml: {
groupLabel: 'SAML',
okta: 'Okta Workforce',
- customSaml: 'Custom SAML Provider',
+ customSaml: undefined,
},
- warning: 'Once a provider is selected you cannot change again until the configuration is over',
+ warning: undefined,
},As per coding guidelines, "Confirm that new translations are accurate, contextually appropriate, and match the intent of the original English strings."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/localizations/src/kk-KZ.ts` around lines 185 - 198, The
selectProviderStep localization object contains English strings that must be
translated to Kazakh; update the selectProviderStep.title, subtitle, body.title,
body.description, saml.groupLabel, saml.okta, saml.customSaml, and warning in
the kk-KZ.ts file to Kazakh equivalents (or add an agreed temporary fallback
translation) so the locale no longer renders mixed-language UI; ensure you edit
the selectProviderStep object and preserve the same keys and structure.
| const createConnectionFetcher = React.useCallback( | ||
| async (selectedProvider: ProviderType) => { | ||
| if (enterpriseConnection) { | ||
| return; | ||
| } | ||
| if (!user?.primaryEmailAddress) { | ||
| throw new Error('Primary email required'); | ||
| } | ||
|
|
||
| const emailDomain = user.primaryEmailAddress.emailAddress.split('@')[1]; | ||
| const organizationId = session?.lastActiveOrganizationId ?? null; | ||
|
|
||
| await createEnterpriseConnection({ | ||
| provider: selectedProvider, | ||
| name: emailDomain, | ||
| organizationId, | ||
| }); | ||
| }, |
There was a problem hiding this comment.
createConnection is not truly idempotent and can double-create connections.
enterpriseConnection is checked from props only, so repeated invocations before parent state refresh can call createEnterpriseConnection multiple times. That violates the intended idempotency and risks duplicate writes (with downstream stale/empty Configure step data).
Suggested fix
+ const hasCreatedConnectionRef = React.useRef(Boolean(enterpriseConnection));
+
+ React.useEffect(() => {
+ if (enterpriseConnection) {
+ hasCreatedConnectionRef.current = true;
+ }
+ }, [enterpriseConnection]);
+
const createConnectionFetcher = React.useCallback(
async (selectedProvider: ProviderType) => {
- if (enterpriseConnection) {
+ if (hasCreatedConnectionRef.current || enterpriseConnection) {
return;
}
if (!user?.primaryEmailAddress) {
throw new Error('Primary email required');
}
@@
await createEnterpriseConnection({
provider: selectedProvider,
name: emailDomain,
organizationId,
});
+ hasCreatedConnectionRef.current = true;
},
[enterpriseConnection, user, session, createEnterpriseConnection],
);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx` around lines
59 - 76, The createConnectionFetcher function can call
createEnterpriseConnection multiple times before parent state updates because it
only checks the props enterpriseConnection; add an in-flight guard to make it
truly idempotent by introducing a local flag/ref (e.g., isCreatingRef) that is
checked at the start of createConnectionFetcher and set to true before calling
createEnterpriseConnection and cleared in finally, and also ensure you
short-circuit if createEnterpriseConnection returns an existing/duplicate
connection; keep the existing user.primaryEmailAddress and
session.lastActiveOrganizationId logic but use the guard around the
createEnterpriseConnection call (references: createConnectionFetcher,
enterpriseConnection, createEnterpriseConnection, user.primaryEmailAddress,
session.lastActiveOrganizationId).
Replace placeholder text in SelectProviderStep with a SAML provider selection UI featuring Okta Workforce and Custom SAML Provider cards. Introduces a ProviderCard component with selected state styling and adjusts Step header alignment to top-align with refined spacing.
…O step - Add local useState for the selected provider (okta | custom_saml) - Render real provider icons via iconImageUrl from img.clerk.com CDN - Gate Step.Footer.Continue on selection - Add provider lock-in warning Alert below the SAML group - Drop placeholder icon Box and unused background color
Covers mount, tile labels and CDN icons, selection state via aria-pressed, Continue gating, and goNext invocation on Continue.
…der card Replace `<Box as='span'>` with the dedicated `<Span>` primitive for the provider icon wrapper in `SelectProviderStep`, simplifying the markup and imports.
…gureSSO Wire every user-visible string in the Select Provider step through @clerk/localizations. Adds a selectProviderStep namespace under __experimental_configureSSO with title, subtitle, body title/description, SAML group label + provider labels, and the lock-in warning. Populates en-US and translates to all ~49 supported locales.
Adds provider/setProvider/clearProvider/createConnection to the flow context so the Select Provider step can persist the chosen provider and create the enterprise connection on Continue. createConnection is wrapped in useReverification and is idempotent — it no-ops when an enterprise connection already exists.
Pure helper that returns the wizard step a user should mount on based on the state of their enterprise connection. No connection lands on Select Provider, an existing connection without SAML IdP metadata lands on Configure, and a fully configured connection lands on Confirmation. Covered by a table-driven unit test.
Wires Wizard.initialStepId via deriveInitialStep so the ConfigureSSO wizard re-mounts on the right step after reload. Renames the local ProviderType ids in Select Provider from 'okta' / 'custom_saml' to the backend values 'saml_okta' / 'saml_custom' so the Continue handler can forward them directly. Continue now sets the provider on the flow context, calls createConnection, and advances the wizard — wrapped in useCardState for loading and error display following the same pattern as Verify Domain.
Mocks the ConfigureSSO flow context so the Continue handler can be verified end-to-end. Asserts setProvider is called with the backend provider id, followed by createConnection, then goNext, and adds coverage for the loading state and the rejection path where the wizard must not advance and the error must surface in the card.
Notes the flow-context extensions, deriveInitialStep helper, and the createConnection-on-Continue behavior so the published changelog matches what the PR actually ships.
Move ProviderType and WizardStepId into a shared types module and lift initialStepId derivation into the flow context so consumers read a single value instead of recomputing it. Replace the flat PROVIDER_OPTIONS list with grouped PROVIDER_GROUPS to support multiple provider categories, switch connection creation to __internal_useUserEnterpriseConnections, and drop the unused clearProvider/goPrev affordances on the Select Provider step.
…ification - createConnection now accepts the provider as a parameter instead of reading it from React state (which was stale within the same tick as setProvider) - Restore the useReverification wrap on createConnection, matching the rest of @clerk/ui's user.* mutation pattern (createEmailAddress, delete, createTOTP, setPrimary, createWeb3Wallet, …) — handles backend session-reverification policy transparently when present, no-ops otherwise
…ureSSO - Hoist the __internal_useUserEnterpriseConnections call to ConfigureSSOCardContent so the provider receives the data and the create mutation as props instead of calling the hook a second time - Drop the unused isLoading from the provider surface; the load gate upstream already consumes it - Rename ConfigureSSOFlowProvider → ConfigureSSOProvider, useConfigureSSOFlow → useConfigureSSO, drop the leftover "Flow" suffix
Pull the enterprise connection from ConfigureSSOContext inside ConfigureStep and render its SAML ACS URL as the single sign-on URL, replacing the placeholder text. Drop the now-unused isLastStep guard from the Select Provider continue button.
6d20749 to
2529d2b
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/localizations/src/ta-IN.ts`:
- Around line 189-202: The new configureSSO.selectProviderStep strings in ta-IN
are still in English; update the selectProviderStep object (keys: title,
subtitle, body.title, body.description, saml.groupLabel, saml.okta,
saml.customSaml, and warning) with accurate Tamil translations that match the
original meanings and UI context (e.g., "Select provider", "Select the provider
you are going to setup SSO for.", "Select your identity provider", "We'll guide
you through the detailed setup process next.", SAML group label, "Okta
Workforce", "Custom SAML Provider", and the warning about not being able to
change provider). Keep phrasing concise and natural for Tamil UI, and verify
translations for tone and clarity before committing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: e475d23a-a182-4164-af5b-10b95b32aa45
📒 Files selected for processing (60)
.changeset/configure-sso-select-provider-step.mdpackages/localizations/src/ar-SA.tspackages/localizations/src/be-BY.tspackages/localizations/src/bg-BG.tspackages/localizations/src/bn-IN.tspackages/localizations/src/ca-ES.tspackages/localizations/src/cs-CZ.tspackages/localizations/src/da-DK.tspackages/localizations/src/de-DE.tspackages/localizations/src/el-GR.tspackages/localizations/src/en-GB.tspackages/localizations/src/en-US.tspackages/localizations/src/es-CR.tspackages/localizations/src/es-ES.tspackages/localizations/src/es-MX.tspackages/localizations/src/es-UY.tspackages/localizations/src/fa-IR.tspackages/localizations/src/fi-FI.tspackages/localizations/src/fr-FR.tspackages/localizations/src/he-IL.tspackages/localizations/src/hi-IN.tspackages/localizations/src/hr-HR.tspackages/localizations/src/hu-HU.tspackages/localizations/src/id-ID.tspackages/localizations/src/is-IS.tspackages/localizations/src/it-IT.tspackages/localizations/src/ja-JP.tspackages/localizations/src/kk-KZ.tspackages/localizations/src/ko-KR.tspackages/localizations/src/mn-MN.tspackages/localizations/src/ms-MY.tspackages/localizations/src/nb-NO.tspackages/localizations/src/nl-BE.tspackages/localizations/src/nl-NL.tspackages/localizations/src/pl-PL.tspackages/localizations/src/pt-BR.tspackages/localizations/src/pt-PT.tspackages/localizations/src/ro-RO.tspackages/localizations/src/ru-RU.tspackages/localizations/src/sk-SK.tspackages/localizations/src/sr-RS.tspackages/localizations/src/sv-SE.tspackages/localizations/src/ta-IN.tspackages/localizations/src/te-IN.tspackages/localizations/src/th-TH.tspackages/localizations/src/tr-TR.tspackages/localizations/src/uk-UA.tspackages/localizations/src/vi-VN.tspackages/localizations/src/zh-CN.tspackages/localizations/src/zh-TW.tspackages/shared/src/types/localization.tspackages/ui/src/components/ConfigureSSO/ConfigureSSO.tsxpackages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsxpackages/ui/src/components/ConfigureSSO/__tests__/deriveInitialStep.test.tspackages/ui/src/components/ConfigureSSO/deriveInitialStep.tspackages/ui/src/components/ConfigureSSO/elements/Step.tsxpackages/ui/src/components/ConfigureSSO/steps/ConfigureStep.tsxpackages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsxpackages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsxpackages/ui/src/components/ConfigureSSO/types.ts
✅ Files skipped from review due to trivial changes (31)
- packages/localizations/src/bn-IN.ts
- .changeset/configure-sso-select-provider-step.md
- packages/ui/src/components/ConfigureSSO/types.ts
- packages/localizations/src/hr-HR.ts
- packages/localizations/src/es-UY.ts
- packages/localizations/src/ro-RO.ts
- packages/localizations/src/cs-CZ.ts
- packages/localizations/src/is-IS.ts
- packages/localizations/src/be-BY.ts
- packages/localizations/src/en-GB.ts
- packages/localizations/src/bg-BG.ts
- packages/localizations/src/fa-IR.ts
- packages/localizations/src/es-MX.ts
- packages/localizations/src/sr-RS.ts
- packages/localizations/src/mn-MN.ts
- packages/localizations/src/pt-BR.ts
- packages/localizations/src/el-GR.ts
- packages/localizations/src/kk-KZ.ts
- packages/localizations/src/nl-NL.ts
- packages/localizations/src/ms-MY.ts
- packages/localizations/src/sk-SK.ts
- packages/localizations/src/id-ID.ts
- packages/localizations/src/he-IL.ts
- packages/localizations/src/sv-SE.ts
- packages/localizations/src/hu-HU.ts
- packages/localizations/src/te-IN.ts
- packages/localizations/src/pt-PT.ts
- packages/localizations/src/ja-JP.ts
- packages/localizations/src/nb-NO.ts
- packages/localizations/src/da-DK.ts
- packages/localizations/src/hi-IN.ts
🚧 Files skipped from review as they are similar to previous changes (23)
- packages/localizations/src/uk-UA.ts
- packages/localizations/src/ar-SA.ts
- packages/ui/src/components/ConfigureSSO/elements/Step.tsx
- packages/ui/src/components/ConfigureSSO/tests/deriveInitialStep.test.ts
- packages/localizations/src/ru-RU.ts
- packages/ui/src/components/ConfigureSSO/deriveInitialStep.ts
- packages/localizations/src/es-ES.ts
- packages/localizations/src/fr-FR.ts
- packages/ui/src/components/ConfigureSSO/steps/ConfigureStep.tsx
- packages/localizations/src/en-US.ts
- packages/localizations/src/th-TH.ts
- packages/localizations/src/es-CR.ts
- packages/localizations/src/vi-VN.ts
- packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx
- packages/localizations/src/zh-CN.ts
- packages/localizations/src/nl-BE.ts
- packages/shared/src/types/localization.ts
- packages/localizations/src/tr-TR.ts
- packages/localizations/src/ko-KR.ts
- packages/localizations/src/fi-FI.ts
- packages/ui/src/components/ConfigureSSO/steps/tests/SelectProviderStep.test.tsx
- packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx
- packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx
| selectProviderStep: { | ||
| title: 'Select provider', | ||
| subtitle: 'Select the provider you are going to setup SSO for.', | ||
| body: { | ||
| title: 'Select your identity provider', | ||
| description: "We'll guide you through the detailed setup process next.", | ||
| }, | ||
| saml: { | ||
| groupLabel: 'SAML', | ||
| okta: 'Okta Workforce', | ||
| customSaml: 'Custom SAML Provider', | ||
| }, | ||
| warning: 'Once a provider is selected you cannot change again until the configuration is over', | ||
| }, |
There was a problem hiding this comment.
ta-IN new SSO provider strings are not translated (still English).
Line 190 through Line 201 add configureSSO.selectProviderStep values in English, which causes mixed-language UI in the Tamil locale and leaves the new keys effectively untranslated for ta-IN. Please provide Tamil translations for this block before merge.
As per coding guidelines: “Confirm that new translations are accurate, contextually appropriate, and match the intent of the original English strings” and “If you notice missing translations for new keys, flag them for completion.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/localizations/src/ta-IN.ts` around lines 189 - 202, The new
configureSSO.selectProviderStep strings in ta-IN are still in English; update
the selectProviderStep object (keys: title, subtitle, body.title,
body.description, saml.groupLabel, saml.okta, saml.customSaml, and warning) with
accurate Tamil translations that match the original meanings and UI context
(e.g., "Select provider", "Select the provider you are going to setup SSO for.",
"Select your identity provider", "We'll guide you through the detailed setup
process next.", SAML group label, "Okta Workforce", "Custom SAML Provider", and
the warning about not being able to change provider). Keep phrasing concise and
natural for Tamil UI, and verify translations for tone and clarity before
committing.
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
Summary
Implements the first wizard step of
<__experimental_ConfigureSSO />— the provider selection screen — and the state machine foundation that makes it work end-to-end. The user picks one of two SAML providers (Okta Workforce, Custom SAML Provider), Continue creates the enterprise connection, and the wizard advances. On reload,deriveInitialSteplands the user on the correct step based on the connection's state. All user-visible copy is wired through@clerk/localizationswith translations for every supported locale. No public component API change.Linear: ORGS-1462
Why
After the layered architecture landed in #8493,
SelectProviderStepwas registered as a regular<Wizard.Step>with a placeholder body. The step is structurally a wizard step (not a pre-wizard gate), but is filtered out of the breadcrumb inConfigureSSOHeaderby id so the visible step count stays at 4 (Verify domain → Configure → Test → Confirmation). This PR fills in the body with the real provider tiles, selection behavior, the localization plumbing — and the flow-context plumbing the rest of the wizard depends on. ThecreateConnectioncall lands here as an interim location; a small follow-up will move it into Verify Domain'sEnterVerificationCodeStep.onResolveonce Laura's #8520 merges.What changed
Select Provider step UI
steps/SelectProviderStep.tsx— replaces the placeholder body with:Select your identity provider+ helper copy)SAMLgroup containing two equal-width tiles laid out viaGrid columns={2}<Alert variant='warning'>) about provider lock-inuseState<ProviderType | null>selection stateuseCardStateStep.Footer.Continuegated onisDisabled={isLastStep || !selected || card.isLoading}; shows a loading state during the async create callStep.Footer.Previousleft as-is (auto-disabled byisFirstStep)localizationKeys('configureSSO.selectProviderStep.*')(no inline literals)ProviderCard(private to the file) —<SimpleButton variant='outline'>with a column-flex icon + label, height$32, gap$2, padding$1x5. Selected state renders a 4pxboxShadowring usingtheme.colors.$colorRing.aria-pressedmirrors the selected state for assistive tech.img.clerk.comstatic CDN viaiconImageUrl('okta')andiconImageUrl('saml'). Rendered as an inline<Span aria-hidden>withbackgroundImage, mirroringProviderIcon's non-mask-image branch — we don't wrap with<ProviderIcon>directly because itsidprop union doesn't include'saml_custom'.elements/Step.tsx— minorStep.Headerpolish: top-align the inner row (align='start') and bump the title/description column gap to$2to match the Figma. No behavior change.State machine foundation
ConfigureSSOContext.tsx— extends the flow context withprovider,setProvider,clearProvider, andcreateConnection.providerderives fromenterpriseConnection?.provider ?? localProvider.createConnectionis wrapped inuseReverification, idempotent (no-ops when a connection already exists), and assembles the payload from the user's primary email domain + active organization id.deriveInitialStep.ts(new) — pure helper that maps a connection state to the wizard step the user should land on: no connection →select-provider, nosamlConnection.idpSsoUrl→configure, otherwise →confirmation. Test step derivation is deferred (no synchronous "last test passed" signal).ConfigureSSO.tsx— wiresWizard.initialStepId={deriveInitialStep(enterpriseConnection)}so reloads land on the right step.SelectProviderStepContinue handler nowsetProvider(selected) → createConnection() → goNext(), all wrapped inuseCardState(setLoading/setIdle/setError) and routed throughhandleErroron rejection.ProviderTyperenamed from UI-side ('okta' | 'custom_saml') to backend values ('saml_okta' | 'saml_custom') so the flow context, the resource call, and the resource's ownproviderfield all agree.Localization
@clerk/shared— adds the typedselectProviderStepnamespace to__internal_LocalizationResource(under__experimental_configureSSO).@clerk/localizations— adds English values inen-US.tsand translations for every other supported locale (~49 files).Out of scope — planned follow-ups
createConnectioninto Verify Domain. Once chore(ui,shared,localizations): Add verify/add email address step to<ConfigureSSO />#8520 merges, the create call moves toEnterVerificationCodeStep.onResolveso the connection is created after email + OTP verification, not on Select Provider Continue. ThecreateConnectionidempotency guard in this PR is what enables that move safely.Test plan
<__experimental_ConfigureSSO />and lands on Select Provider on cold start (deriveInitialStepreturnsselect-provider)img.clerk.comaria-pressedflips on the selected tile (verified via DevTools accessibility tree)setProvider+createConnection+goNextinvoked in order, loading state, error pathderiveInitialSteptable-driven tests cover undefined connection / nosamlConnection/ noidpSsoUrl/ hasidpSsoUrlType of change
__experimental_*surface — no public API change)