Skip to content

feat(ui): Add Google Workspace SAML to self-serve SSO#8690

Merged
brkalow merged 12 commits into
mainfrom
laura/orgs-1533-sdk-add-support-for-google-workspace-saml
Jun 1, 2026
Merged

feat(ui): Add Google Workspace SAML to self-serve SSO#8690
brkalow merged 12 commits into
mainfrom
laura/orgs-1533-sdk-add-support-for-google-workspace-saml

Conversation

@LauraBeatris
Copy link
Copy Markdown
Member

@LauraBeatris LauraBeatris commented May 28, 2026

Description

Introduces Google Workspace SAML provider with tailored configure steps

CleanShot.2026-05-28.at.14.28.17.mp4

Unrelated changes/fixes

  • Update stepper line-height to fix alignment issue
  • Update test runs query to not refetch on window focus, which was causing the 'Refresh logs' button to display a spinner on mount

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 1, 2026 7:42pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 28, 2026

🦋 Changeset detected

Latest commit: 3ba2066

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@clerk/localizations Patch
@clerk/shared Patch
@clerk/ui Patch
@clerk/react Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/chrome-extension Patch
@clerk/clerk-js Patch
@clerk/expo-passkeys Patch
@clerk/expo Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/msw Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/vue Patch

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 28, 2026

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8690

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8690

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8690

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8690

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8690

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8690

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8690

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8690

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8690

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8690

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8690

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8690

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8690

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8690

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8690

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8690

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8690

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8690

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8690

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8690

commit: 3ba2066

@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from 3fe1741 to b407275 Compare May 28, 2026 15:45
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from b407275 to f6e8d22 Compare May 28, 2026 15:47
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from f6e8d22 to 20ffc64 Compare May 28, 2026 15:56
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from 20ffc64 to e22691d Compare May 28, 2026 16:10
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from e22691d to 1e68b4e Compare May 28, 2026 16:13
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from 0db7c4c to 10ce4d7 Compare May 28, 2026 17:03
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from f5f6f55 to a1018c2 Compare May 28, 2026 17:30
@LauraBeatris LauraBeatris marked this pull request as ready for review May 28, 2026 17:30
@LauraBeatris LauraBeatris requested review from a team and iagodahlem May 28, 2026 17:30
@github-actions
Copy link
Copy Markdown
Contributor

API Changes Report

Generated by snapi on 2026-05-28T17:33:02.523Z

Summary

Metric Count
Packages analyzed 6
Packages with changes 1
🔴 Breaking changes 0
🟡 Non-breaking changes 2
🟢 Additions 0

🤖 This report was reviewed by claude-sonnet-4-6.


@clerk/shared

Current version: 4.14.0
Recommended bump: MINOR → 4.15.0

Subpath ./types

🟡 Non-breaking Changes (2)

Modified: __internal_LocalizationResource
Diff (before: 1680 lines, after: 1783 lines). Click to expand.
// ... 1215 unchanged lines elided ...
          groupLabel: LocalizationValue;
          okta: LocalizationValue;
          customSaml: LocalizationValue;
+         google: LocalizationValue;
        };
        warning: LocalizationValue;
      };
      verifyEmailDomainStep: {
        title: LocalizationValue;
        subtitle: LocalizationValue;
        addEmailAddress: {
          formTitle: LocalizationValue;
          formSubtitle: LocalizationValue;
          inputPlaceholder: LocalizationValue;
          inputLabel: LocalizationValue;
        };
        emailCode: {
          formTitle: LocalizationValue;
          formSubtitle: LocalizationValue<'identifier'>;
          resendButton: LocalizationValue;
          verified: {
            title: LocalizationValue;
            subtitle: LocalizationValue;
            inputLabel: LocalizationValue;
          };
        };
        domainTaken: {
          title: LocalizationValue<'domain'>;
          subtitle: LocalizationValue;
        };
      };
      testConfigurationStep: {
        title: LocalizationValue;
        subtitle: LocalizationValue;
        error__noSuccessfulTestRun: LocalizationValue;
        testUrl: {
          actionLabel__open: LocalizationValue;
        };
        testResults: {
          title: LocalizationValue;
          actionLabel__refresh: LocalizationValue;
          polling: LocalizationValue;
          status__success: LocalizationValue;
          status__failed: LocalizationValue;
          status__pending: LocalizationValue;
          empty: {
            title: LocalizationValue;
            subtitle: LocalizationValue;
          };
        };
        testRunDetails: {
          title: LocalizationValue;
          runDetails: {
            sectionTitle: LocalizationValue;
            timestamp: LocalizationValue;
            status: LocalizationValue;
            errorCode: LocalizationValue;
            fullMessage: LocalizationValue;
            actionLabel__copy: LocalizationValue;
            actionLabel__copied: LocalizationValue;
          };
          parsedUserInfo: {
            sectionTitle: LocalizationValue;
            email: LocalizationValue;
            firstName: LocalizationValue;
          };
          howToFix: {
            sectionTitle: LocalizationValue;
            actionLabel__viewDocumentation: LocalizationValue;
            saml_user_attribute_missing: {
              intro: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
              step3: LocalizationValue;
            };
            saml_response_relaystate_missing: {
              description: LocalizationValue;
            };
            saml_email_address_domain_mismatch: {
              description: LocalizationValue;
            };
            oauth_access_denied: {
              description: LocalizationValue;
            };
            oauth_token_exchange_error: {
              description: LocalizationValue;
            };
            oauth_fetch_user_error: {
              intro: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
            };
          };
        };
      };
      configureStep: {
        attributeMappingTable: {
          badges: {
            required: LocalizationValue;
            optional: LocalizationValue;
          };
        };
        samlOkta: {
          mainHeaderTitle: LocalizationValue;
          createAppStep: {
            headerSubtitle: LocalizationValue;
            createAppInstructions: {
              title: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
              step3: LocalizationValue;
              step4: LocalizationValue;
              step5: LocalizationValue;
            };
            serviceProviderInstructions: {
              title: LocalizationValue;
              paragraph1: LocalizationValue;
              paragraph2: LocalizationValue;
              serviceProviderFields: {
                acsUrl: {
                  label: LocalizationValue;
                };
                spEntityId: {
                  label: LocalizationValue;
                };
              };
            };
            completeSamlIntegrationInstructions: {
              title: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
            };
          };
          attributeMappingStep: {
            headerSubtitle: LocalizationValue;
            paragraph: LocalizationValue;
            step1: LocalizationValue;
            step2: LocalizationValue;
            attributeMappingTable: {
              columns: {
                name: LocalizationValue;
                expression: LocalizationValue;
              };
              rows: {
                email: {
                  name: LocalizationValue;
                  expression: LocalizationValue;
                };
                firstName: {
                  name: LocalizationValue;
                  expression: LocalizationValue;
                };
                lastName: {
                  name: LocalizationValue;
                  expression: LocalizationValue;
                };
              };
            };
          };
          assignUsersStep: {
            headerSubtitle: LocalizationValue;
            assignUsersInstructions: {
              title: LocalizationValue;
              paragraph: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
              step3: LocalizationValue;
              step4: LocalizationValue;
              step5: LocalizationValue;
            };
          };
          identityProviderMetadataStep: {
            headerSubtitle: LocalizationValue;
            modes: {
              title: LocalizationValue;
              ariaLabel: LocalizationValue;
              metadataUrl: LocalizationValue;
              manual: LocalizationValue;
            };
            metadataUrl: {
              label: LocalizationValue;
              placeholder: LocalizationValue;
              description: LocalizationValue;
            };
            manual: {
              description: LocalizationValue;
              signOnUrl: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              issuer: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              signingCertificate: {
                label: LocalizationValue;
                uploadFile: LocalizationValue;
                replaceFile: LocalizationValue;
                removeFile: LocalizationValue;
                fileUploaded: LocalizationValue;
              };
            };
          };
        };
        samlCustom: {
          mainHeaderTitle: LocalizationValue;
          createAppStep: {
            headerSubtitle: LocalizationValue;
            createAppInstructions: {
              title: LocalizationValue;
              paragraph: LocalizationValue;
            };
            serviceProviderFields: {
              acsUrl: {
                label: LocalizationValue;
              };
              spEntityId: {
                label: LocalizationValue;
              };
            };
          };
          attributeMappingStep: {
            headerSubtitle: LocalizationValue;
            paragraph: LocalizationValue;
            attributeMappingTable: {
              title: LocalizationValue;
              columns: {
                userProfile: LocalizationValue;
                attributeName: LocalizationValue;
              };
              rows: {
                email: {
                  userProfile: LocalizationValue;
                  attributeName: LocalizationValue;
                };
                firstName: {
                  userProfile: LocalizationValue;
                  attributeName: LocalizationValue;
                };
                lastName: {
                  userProfile: LocalizationValue;
                  attributeName: LocalizationValue;
                };
              };
            };
          };
          assignUsersStep: {
            headerSubtitle: LocalizationValue;
            title: LocalizationValue;
            paragraph: LocalizationValue;
          };
          identityProviderMetadataStep: {
            headerSubtitle: LocalizationValue;
            modes: {
              title: LocalizationValue;
              ariaLabel: LocalizationValue;
              metadataUrl: LocalizationValue;
              manual: LocalizationValue;
            };
            metadataUrl: {
              label: LocalizationValue;
              placeholder: LocalizationValue;
+             description: LocalizationValue;
+           };
+           manual: {
+             description: LocalizationValue;
+             signOnUrl: {
+               label: LocalizationValue;
+               placeholder: LocalizationValue;
+             };
+             issuer: {
+               label: LocalizationValue;
+               placeholder: LocalizationValue;
+             };
+             signingCertificate: {
+               label: LocalizationValue;
+               uploadFile: LocalizationValue;
+               replaceFile: LocalizationValue;
+               removeFile: LocalizationValue;
+               fileUploaded: LocalizationValue;
+             };
+           };
+         };
+       };
+       samlGoogle: {
+         mainHeaderTitle: LocalizationValue;
+         createAppStep: {
+           headerSubtitle: LocalizationValue;
+           createAppInstructions: {
+             title: LocalizationValue;
+             step1: LocalizationValue;
+             step2: LocalizationValue;
+             step3: LocalizationValue;
+             step4: LocalizationValue;
+             step5: LocalizationValue;
+           };
+         };
+         identityProviderMetadataStep: {
+           headerSubtitle: LocalizationValue;
+           modes: {
+             title: LocalizationValue;
+             ariaLabel: LocalizationValue;
+             metadataFile: LocalizationValue;
+             manual: LocalizationValue;
+           };
+           metadataFile: {
+             label: LocalizationValue;
              description: LocalizationValue;
+             uploadFile: LocalizationValue;
+             replaceFile: LocalizationValue;
+             removeFile: LocalizationValue;
+             fileUploaded: LocalizationValue;
            };
            manual: {
              description: LocalizationValue;
              signOnUrl: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              issuer: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              signingCertificate: {
                label: LocalizationValue;
                uploadFile: LocalizationValue;
                replaceFile: LocalizationValue;
                removeFile: LocalizationValue;
                fileUploaded: LocalizationValue;
+             };
+           };
+         };
+         serviceProviderStep: {
+           headerSubtitle: LocalizationValue;
+           title: LocalizationValue;
+           paragraph: LocalizationValue;
+           serviceProviderFields: {
+             acsUrl: {
+               label: LocalizationValue;
+             };
+             spEntityId: {
+               label: LocalizationValue;
+             };
+           };
+           nameIdInstructions: {
+             step1: LocalizationValue;
+             step2: LocalizationValue;
+           };
+         };
+         attributeMappingStep: {
+           headerSubtitle: LocalizationValue;
+           paragraph: LocalizationValue;
+           step1: LocalizationValue;
+           step2: LocalizationValue;
+           attributeMappingTable: {
+             columns: {
+               googleAttribute: LocalizationValue;
+               appAttribute: LocalizationValue;
+             };
+             rows: {
+               email: {
+                 googleAttribute: LocalizationValue;
+                 appAttribute: LocalizationValue;
+               };
+               firstName: {
+                 googleAttribute: LocalizationValue;
+                 appAttribute: LocalizationValue;
+               };
+               lastName: {
+                 googleAttribute: LocalizationValue;
+                 appAttribute: LocalizationValue;
+               };
              };
+           };
+         };
+         configureUserAccess: {
+           headerSubtitle: LocalizationValue;
+           assignUsersInstructions: {
+             paragraph1: LocalizationValue;
+             step1: LocalizationValue;
+             step2: LocalizationValue;
+             step3: LocalizationValue;
+             paragraph2: LocalizationValue;
            };
          };
        };
// ... 182 unchanged lines elided ...

Static analyzer: Breaking change in type alias __internal_LocalizationResource: Type changed: {locale:string;maintenanceMode:LocalizationValue;roles:{[r:string]:LocalizationValue;};socialButtonsBlockButton:Localiz…{locale:string;maintenanceMode:LocalizationValue;roles:{[r:string]:LocalizationValue;};socialButtonsBlockButton:Localiz…

🤖 AI review (reclassified as non-breaking) (85%): The __internal_LocalizationResource type had new fields added to it (e.g., configureSSO.selectProviderStep.saml.google, configureSSO.configureStep.samlGoogle, and other nested additions). This type is used as an input/source for LocalizationResource (via DeepPartial<DeepLocalizationWithoutObjects<__internal_LocalizationResource>>), meaning consumers providing localization objects only need to supply a subset. Adding new optional keys to the shape is non-breaking for existing consumers. However, the rule-based analyzer flagged it as breaking. Since LocalizationResource extends DeepPartial<...>, existing consumer code that provides a partial localization object would still compile fine with the additions.

Modified: FieldId
- type FieldId = 'firstName' | 'lastName' | 'name' | 'slug' | 'emailAddress' | 'phoneNumber' | 'currentPassword' | 'newPassword' | 'signOutOfOtherSessions' | 'passkeyName' | 'password' | 'confirmPassword' | 'identifier' | 'username' | 'code' | 'role' | 'deleteConfirmation' | 'deleteOrganizationConfirmation' | 'enrollmentMode' | 'affiliationEmailAddress' | 'deleteExistingInvitationsSuggestions' | 'legalAccepted' | 'apiKeyDescription' | 'apiKeyExpirationDate' | 'apiKeyRevokeConfirmation' | 'apiKeySecret' | 'idpCertificate' | 'idpEntityId' | 'idpMetadataUrl' | 'idpSsoUrl' | 'acsUrl' | 'spEntityId' | 'web3WalletName';
+ type FieldId = 'firstName' | 'lastName' | 'name' | 'slug' | 'emailAddress' | 'phoneNumber' | 'currentPassword' | 'newPassword' | 'signOutOfOtherSessions' | 'passkeyName' | 'password' | 'confirmPassword' | 'identifier' | 'username' | 'code' | 'role' | 'deleteConfirmation' | 'deleteOrganizationConfirmation' | 'enrollmentMode' | 'affiliationEmailAddress' | 'deleteExistingInvitationsSuggestions' | 'legalAccepted' | 'apiKeyDescription' | 'apiKeyExpirationDate' | 'apiKeyRevokeConfirmation' | 'apiKeySecret' | 'idpCertificate' | 'idpEntityId' | 'idpMetadata' | 'idpMetadataUrl' | 'idpSsoUrl' | 'acsUrl' | 'spEntityId' | 'web3WalletName';

Static analyzer: Breaking change in type alias FieldId: Type changed: 'firstName'|'lastName'|'name'|'slug'|'emailAddress'|'phoneNumber'|'currentPassword'|'newPassword'|'signOutOfOtherSessio…'firstName'|'lastName'|'name'|'slug'|'emailAddress'|'phoneNumber'|'currentPassword'|'newPassword'|'signOutOfOtherSessio…

🤖 AI review (reclassified as non-breaking) (80%): The FieldId type had 'idpMetadata' added as a new member to the union. FieldId is used internally as a field identifier type. Consumers who exhaustively switch over FieldId values (e.g., via a switch statement with no default) could be affected, but adding a new union member to a string union that is used as an identifier/label type is generally non-breaking for consumers that receive values of this type. Consumers who produce values typed as FieldId are unaffected since the new string is simply an additional valid value.


Report generated by snapi

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 869acfe1-d600-441b-a381-8b82ef477ce9

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0378b and 3ba2066.

📒 Files selected for processing (18)
  • .changeset/beige-breads-bathe.md
  • packages/localizations/src/en-US.ts
  • packages/shared/src/react/hooks/useEnterpriseConnectionTestRuns.tsx
  • packages/shared/src/types/elementIds.ts
  • packages/shared/src/types/localization.ts
  • packages/ui/src/components/ConfigureSSO/elements/Stepper/Stepper.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/index.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/index.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationModes.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderMetadataForm.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/useIdentityProviderMetadataForm.ts
  • packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx
  • packages/ui/src/components/ConfigureSSO/types.ts
💤 Files with no reviewable changes (2)
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/useIdentityProviderMetadataForm.ts
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderMetadataForm.tsx
✅ Files skipped from review due to trivial changes (2)
  • packages/shared/src/types/elementIds.ts
  • .changeset/beige-breads-bathe.md
🚧 Files skipped from review as they are similar to previous changes (13)
  • packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx
  • packages/shared/src/react/hooks/useEnterpriseConnectionTestRuns.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/index.tsx
  • packages/ui/src/components/ConfigureSSO/steps/tests/SelectProviderStep.test.tsx
  • packages/ui/src/components/ConfigureSSO/types.ts
  • packages/ui/src/components/ConfigureSSO/elements/Stepper/Stepper.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationModes.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/index.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx
  • packages/shared/src/types/localization.ts
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx
  • packages/localizations/src/en-US.ts

📝 Walkthrough

Walkthrough

This PR adds Google Workspace as a new SAML provider option to the self-serve ConfigureSSO wizard. It introduces shared form infrastructure (IdentityProviderConfigurationForm, IdentityProviderConfigurationModes) to handle SAML configuration across multiple providers, refactors existing Okta and Custom SAML steps to use this infrastructure, and implements a new multi-step Google SAML configuration flow. Type definitions, localization keys, and English localization strings are extended to support the new provider. The old controller-based metadata form and hook were removed in favor of local component state and the shared form.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • clerk/javascript#8544: Related changes to the useEnterpriseConnectionTestRuns hook behavior adjacent to this PR’s hook tweak.
  • clerk/javascript#8601: Overlaps on ConfigureSSO select provider edits and localization additions similar to this PR.
  • clerk/javascript#8651: Builds on the same per-IdP ConfigureSSO architecture that this PR extends.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately summarizes the main change: adding Google Workspace SAML support to self-serve SSO in the UI package.
Description check ✅ Passed The pull request description is clearly related to the changeset, detailing the main feature (Google Workspace SAML provider), unrelated fixes (stepper line-height and test runs query), and build/test status.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx (1)

105-132: ⚡ Quick win

Add a submit-path test for the new Google provider.

These assertions cover rendering/icons, but they don’t verify wiring. Please add a case that selects Google Workspace, clicks Continue, and asserts setProvider('saml_google'), createEnterpriseConnection('saml_google', ...), and navigation to configure.

As per coding guidelines: **/*.{test,spec}.{ts,tsx}: Unit tests are required for all new functionality.

🤖 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/steps/__tests__/SelectProviderStep.test.tsx`
around lines 105 - 132, Add a new test in SelectProviderStep.test.tsx that
simulates selecting the "Google Workspace" radio, clicking the Continue button,
and asserting the wiring: mock and verify setProvider was called with
'saml_google', createEnterpriseConnection was invoked with 'saml_google' and the
expected args, and the app navigated to the "configure" route (assert the mocked
router/navigation push was called with 'configure'); use the existing
createFixtures()/renderStep(wrapper) helpers and the same mocking utilities used
elsewhere in this file to locate and click the Continue button and to spy on
setProvider, createEnterpriseConnection, and the navigation method.
🤖 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/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx`:
- Around line 270-280: The initial mode logic currently sets mode to 'manual'
whenever any SAML config exists (see samlConnection and hasExistingConfig),
which overrides metadata-URL-only setups; change the initializer for the React
state in SamlCustomConfigureSteps (the useState for mode / IdpConfigurationMode)
to prefer 'metadataUrl' when samlConnection?.idpMetadataUrl is present,
otherwise fall back to 'manual' if any other config exists, or 'metadataUrl'
when no config exists per previous behavior; update references to
hasExistingConfig/idpMetadataUrl in the mode initializer accordingly so initial
UI reflects metadata URL setups.

In
`@packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx`:
- Around line 468-478: The initial mode logic currently sets mode to 'manual'
whenever any SAML config exists; change this to prefer 'metadataUrl' when the
saved config only contains idpMetadataUrl. Update the initialization of mode
(the React.useState<IdpConfigurationMode> call) to compute an initialMode that
checks samlConnection: if samlConnection?.idpMetadataUrl is present and
idpEntityId, idpSsoUrl, and idpCertificate are absent then use 'metadataUrl',
otherwise if any other fields or idpCertificate exist use 'manual', and fall
back to 'metadataUrl' when no config exists; keep references to samlConnection,
hasExistingConfig (or replace with the new computed initialMode) and
IdpConfigurationMode to locate the change.

In
`@packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx`:
- Around line 161-168: The certificate upload is currently allowing private key
files via the accept prop ('.key') on the FileUploadField inside
IdentityProviderConfigurationForm; remove '.key' from the accept list (keep
.pem, .crt, .cer, .cert) and add server/client-side validation in the
FileUploadField upload handler (used by form.onCertFileChange / form.certFile)
to reject files that contain private key markers (e.g. "-----BEGIN PRIVATE
KEY-----" or "-----BEGIN RSA PRIVATE KEY-----") and surface a clear validation
error to the user; this ensures the accept prop and the FileUploadField both
prevent private-key uploads.

---

Nitpick comments:
In
`@packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx`:
- Around line 105-132: Add a new test in SelectProviderStep.test.tsx that
simulates selecting the "Google Workspace" radio, clicking the Continue button,
and asserting the wiring: mock and verify setProvider was called with
'saml_google', createEnterpriseConnection was invoked with 'saml_google' and the
expected args, and the app navigated to the "configure" route (assert the mocked
router/navigation push was called with 'configure'); use the existing
createFixtures()/renderStep(wrapper) helpers and the same mocking utilities used
elsewhere in this file to locate and click the Continue button and to spy on
setProvider, createEnterpriseConnection, and the navigation method.
🪄 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: 48c9bbf8-15ce-459e-91a0-1010da42cdcd

📥 Commits

Reviewing files that changed from the base of the PR and between 37535f9 and a1018c2.

📒 Files selected for processing (18)
  • .changeset/beige-breads-bathe.md
  • packages/localizations/src/en-US.ts
  • packages/shared/src/react/hooks/useEnterpriseConnectionTestRuns.tsx
  • packages/shared/src/types/elementIds.ts
  • packages/shared/src/types/localization.ts
  • packages/ui/src/components/ConfigureSSO/elements/Stepper/Stepper.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/index.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/index.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationModes.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderMetadataForm.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/useIdentityProviderMetadataForm.ts
  • packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx
  • packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx
  • packages/ui/src/components/ConfigureSSO/types.ts
💤 Files with no reviewable changes (2)
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderMetadataForm.tsx
  • packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/useIdentityProviderMetadataForm.ts

Comment on lines +161 to +168
<FileUploadField
field={form.certificateField}
file={form.certFile}
onFileChange={form.onCertFileChange}
existingFilePresent={Boolean(form.existingCertPresent)}
labels={labels}
accept='.pem,.key,.crt,.cer,.cert'
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disallow private-key uploads in the certificate field

Line 167 includes .key in accepted extensions. That can lead users to upload private keys as “certificates,” risking secret leakage.

Suggested fix
-      accept='.pem,.key,.crt,.cer,.cert'
+      accept='.pem,.crt,.cer,.cert'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<FileUploadField
field={form.certificateField}
file={form.certFile}
onFileChange={form.onCertFileChange}
existingFilePresent={Boolean(form.existingCertPresent)}
labels={labels}
accept='.pem,.key,.crt,.cer,.cert'
/>
<FileUploadField
field={form.certificateField}
file={form.certFile}
onFileChange={form.onCertFileChange}
existingFilePresent={Boolean(form.existingCertPresent)}
labels={labels}
accept='.pem,.crt,.cer,.cert'
/>
🤖 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/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx`
around lines 161 - 168, The certificate upload is currently allowing private key
files via the accept prop ('.key') on the FileUploadField inside
IdentityProviderConfigurationForm; remove '.key' from the accept list (keep
.pem, .crt, .cer, .cert) and add server/client-side validation in the
FileUploadField upload handler (used by form.onCertFileChange / form.certFile)
to reject files that contain private key markers (e.g. "-----BEGIN PRIVATE
KEY-----" or "-----BEGIN RSA PRIVATE KEY-----") and surface a clear validation
error to the user; this ensures the accept prop and the FileUploadField both
prevent private-key uploads.

@LauraBeatris LauraBeatris changed the title chore(ui): Add Google Workspace SAML to self-serve SSO feat(ui): Add Google Workspace SAML to self-serve SSO May 28, 2026
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from a1018c2 to 9199b6d Compare June 1, 2026 17:46
@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from 9199b6d to e85a885 Compare June 1, 2026 17:47
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

API Changes Report

Generated by Break Check on 2026-06-01T19:45:56.867Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 1
🔴 Breaking changes 2
🟡 Non-breaking changes 3
🟢 Additions 2

Warning
2 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

Note
Break Check could not snapshot 3 subpaths; the diff below excludes them.

  • @clerk/astro ./env: Internal Error: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/shared ./cookie: Internal Error: Unable to follow symbol for "Cookies" You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/testing ./cypress: Symbol not found for identifier: Cypress

@clerk/shared

Current version: 4.14.0
Recommended bump: MAJOR → 5.0.0

Subpath ./apiUrlFromPublishableKey

🟡 Non-breaking Changes (1)

Modified: apiUrlFromPublishableKey
- apiUrlFromPublishableKey: (publishableKey: string) => "https://api.lclclerk.com" | "https://api.clerkstage.dev" | "https://api.clerk.com"
+ apiUrlFromPublishableKey: (publishableKey: string) => "https://api.clerk.com" | "https://api.lclclerk.com" | "https://api.clerkstage.dev"

Static analyzer: Breaking change in function apiUrlFromPublishableKey: Return type changed: "https://api.lclclerk.com"|"https://api.clerkstage.dev"|"https://api.clerk.com""https://api.clerk.com"|"https://api.lclclerk.com"|"https://api.clerkstage.dev"

🤖 AI review (reclassified as non-breaking) (95%): The union type members are identical in both versions — only their order differs. TypeScript union types are order-independent structurally, so no well-typed consumer code would break. A consumer checking returnValue === 'https://api.clerk.com' or exhaustively switching over the union members would behave identically.

Subpath ./index-Dq-_K2VH

🟢 Additions (1)

Added: ./index-Dq-_K2VH

New subpath export ./index-Dq-_K2VH (2781 exported members)

Subpath ./index-ZibUt-Ji

🔴 Breaking Changes (1)

Changed: ./index-ZibUt-Ji

Subpath export ./index-ZibUt-Ji was removed

Subpath ./types

🟡 Non-breaking Changes (2)

Modified: __internal_LocalizationResource
Diff (before: 1680 lines, after: 1783 lines). Click to expand.
// ... 1215 unchanged lines elided ...
          groupLabel: LocalizationValue;
          okta: LocalizationValue;
          customSaml: LocalizationValue;
+         google: LocalizationValue;
        };
        warning: LocalizationValue;
      };
      verifyEmailDomainStep: {
        title: LocalizationValue;
        subtitle: LocalizationValue;
        addEmailAddress: {
          formTitle: LocalizationValue;
          formSubtitle: LocalizationValue;
          inputPlaceholder: LocalizationValue;
          inputLabel: LocalizationValue;
        };
        emailCode: {
          formTitle: LocalizationValue;
          formSubtitle: LocalizationValue<'identifier'>;
          resendButton: LocalizationValue;
          verified: {
            title: LocalizationValue;
            subtitle: LocalizationValue;
            inputLabel: LocalizationValue;
          };
        };
        domainTaken: {
          title: LocalizationValue<'domain'>;
          subtitle: LocalizationValue;
        };
      };
      testConfigurationStep: {
        title: LocalizationValue;
        subtitle: LocalizationValue;
        error__noSuccessfulTestRun: LocalizationValue;
        testUrl: {
          actionLabel__open: LocalizationValue;
        };
        testResults: {
          title: LocalizationValue;
          actionLabel__refresh: LocalizationValue;
          polling: LocalizationValue;
          status__success: LocalizationValue;
          status__failed: LocalizationValue;
          status__pending: LocalizationValue;
          empty: {
            title: LocalizationValue;
            subtitle: LocalizationValue;
          };
        };
        testRunDetails: {
          title: LocalizationValue;
          runDetails: {
            sectionTitle: LocalizationValue;
            timestamp: LocalizationValue;
            status: LocalizationValue;
            errorCode: LocalizationValue;
            fullMessage: LocalizationValue;
            actionLabel__copy: LocalizationValue;
            actionLabel__copied: LocalizationValue;
          };
          parsedUserInfo: {
            sectionTitle: LocalizationValue;
            email: LocalizationValue;
            firstName: LocalizationValue;
          };
          howToFix: {
            sectionTitle: LocalizationValue;
            actionLabel__viewDocumentation: LocalizationValue;
            saml_user_attribute_missing: {
              intro: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
              step3: LocalizationValue;
            };
            saml_response_relaystate_missing: {
              description: LocalizationValue;
            };
            saml_email_address_domain_mismatch: {
              description: LocalizationValue;
            };
            oauth_access_denied: {
              description: LocalizationValue;
            };
            oauth_token_exchange_error: {
              description: LocalizationValue;
            };
            oauth_fetch_user_error: {
              intro: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
            };
          };
        };
      };
      configureStep: {
        attributeMappingTable: {
          badges: {
            required: LocalizationValue;
            optional: LocalizationValue;
          };
        };
        samlOkta: {
          mainHeaderTitle: LocalizationValue;
          createAppStep: {
            headerSubtitle: LocalizationValue;
            createAppInstructions: {
              title: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
              step3: LocalizationValue;
              step4: LocalizationValue;
              step5: LocalizationValue;
            };
            serviceProviderInstructions: {
              title: LocalizationValue;
              paragraph1: LocalizationValue;
              paragraph2: LocalizationValue;
              serviceProviderFields: {
                acsUrl: {
                  label: LocalizationValue;
                };
                spEntityId: {
                  label: LocalizationValue;
                };
              };
            };
            completeSamlIntegrationInstructions: {
              title: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
            };
          };
          attributeMappingStep: {
            headerSubtitle: LocalizationValue;
            paragraph: LocalizationValue;
            step1: LocalizationValue;
            step2: LocalizationValue;
            attributeMappingTable: {
              columns: {
                name: LocalizationValue;
                expression: LocalizationValue;
              };
              rows: {
                email: {
                  name: LocalizationValue;
                  expression: LocalizationValue;
                };
                firstName: {
                  name: LocalizationValue;
                  expression: LocalizationValue;
                };
                lastName: {
                  name: LocalizationValue;
                  expression: LocalizationValue;
                };
              };
            };
          };
          assignUsersStep: {
            headerSubtitle: LocalizationValue;
            assignUsersInstructions: {
              title: LocalizationValue;
              paragraph: LocalizationValue;
              step1: LocalizationValue;
              step2: LocalizationValue;
              step3: LocalizationValue;
              step4: LocalizationValue;
              step5: LocalizationValue;
            };
          };
          identityProviderMetadataStep: {
            headerSubtitle: LocalizationValue;
            modes: {
              title: LocalizationValue;
              ariaLabel: LocalizationValue;
              metadataUrl: LocalizationValue;
              manual: LocalizationValue;
            };
            metadataUrl: {
              label: LocalizationValue;
              placeholder: LocalizationValue;
              description: LocalizationValue;
            };
            manual: {
              description: LocalizationValue;
              signOnUrl: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              issuer: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              signingCertificate: {
                label: LocalizationValue;
                uploadFile: LocalizationValue;
                replaceFile: LocalizationValue;
                removeFile: LocalizationValue;
                fileUploaded: LocalizationValue;
              };
            };
          };
        };
        samlCustom: {
          mainHeaderTitle: LocalizationValue;
          createAppStep: {
            headerSubtitle: LocalizationValue;
            createAppInstructions: {
              title: LocalizationValue;
              paragraph: LocalizationValue;
            };
            serviceProviderFields: {
              acsUrl: {
                label: LocalizationValue;
              };
              spEntityId: {
                label: LocalizationValue;
              };
            };
          };
          attributeMappingStep: {
            headerSubtitle: LocalizationValue;
            paragraph: LocalizationValue;
            attributeMappingTable: {
              title: LocalizationValue;
              columns: {
                userProfile: LocalizationValue;
                attributeName: LocalizationValue;
              };
              rows: {
                email: {
                  userProfile: LocalizationValue;
                  attributeName: LocalizationValue;
                };
                firstName: {
                  userProfile: LocalizationValue;
                  attributeName: LocalizationValue;
                };
                lastName: {
                  userProfile: LocalizationValue;
                  attributeName: LocalizationValue;
                };
              };
            };
          };
          assignUsersStep: {
            headerSubtitle: LocalizationValue;
            title: LocalizationValue;
            paragraph: LocalizationValue;
          };
          identityProviderMetadataStep: {
            headerSubtitle: LocalizationValue;
            modes: {
              title: LocalizationValue;
              ariaLabel: LocalizationValue;
              metadataUrl: LocalizationValue;
              manual: LocalizationValue;
            };
            metadataUrl: {
              label: LocalizationValue;
              placeholder: LocalizationValue;
+             description: LocalizationValue;
+           };
+           manual: {
+             description: LocalizationValue;
+             signOnUrl: {
+               label: LocalizationValue;
+               placeholder: LocalizationValue;
+             };
+             issuer: {
+               label: LocalizationValue;
+               placeholder: LocalizationValue;
+             };
+             signingCertificate: {
+               label: LocalizationValue;
+               uploadFile: LocalizationValue;
+               replaceFile: LocalizationValue;
+               removeFile: LocalizationValue;
+               fileUploaded: LocalizationValue;
+             };
+           };
+         };
+       };
+       samlGoogle: {
+         mainHeaderTitle: LocalizationValue;
+         createAppStep: {
+           headerSubtitle: LocalizationValue;
+           createAppInstructions: {
+             title: LocalizationValue;
+             step1: LocalizationValue;
+             step2: LocalizationValue;
+             step3: LocalizationValue;
+             step4: LocalizationValue;
+             step5: LocalizationValue;
+           };
+         };
+         identityProviderMetadataStep: {
+           headerSubtitle: LocalizationValue;
+           modes: {
+             title: LocalizationValue;
+             ariaLabel: LocalizationValue;
+             metadataFile: LocalizationValue;
+             manual: LocalizationValue;
+           };
+           metadataFile: {
+             label: LocalizationValue;
              description: LocalizationValue;
+             uploadFile: LocalizationValue;
+             replaceFile: LocalizationValue;
+             removeFile: LocalizationValue;
+             fileUploaded: LocalizationValue;
            };
            manual: {
              description: LocalizationValue;
              signOnUrl: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              issuer: {
                label: LocalizationValue;
                placeholder: LocalizationValue;
              };
              signingCertificate: {
                label: LocalizationValue;
                uploadFile: LocalizationValue;
                replaceFile: LocalizationValue;
                removeFile: LocalizationValue;
                fileUploaded: LocalizationValue;
+             };
+           };
+         };
+         serviceProviderStep: {
+           headerSubtitle: LocalizationValue;
+           title: LocalizationValue;
+           paragraph: LocalizationValue;
+           serviceProviderFields: {
+             acsUrl: {
+               label: LocalizationValue;
+             };
+             spEntityId: {
+               label: LocalizationValue;
+             };
+           };
+           nameIdInstructions: {
+             step1: LocalizationValue;
+             step2: LocalizationValue;
+           };
+         };
+         attributeMappingStep: {
+           headerSubtitle: LocalizationValue;
+           paragraph: LocalizationValue;
+           step1: LocalizationValue;
+           step2: LocalizationValue;
+           attributeMappingTable: {
+             columns: {
+               googleAttribute: LocalizationValue;
+               appAttribute: LocalizationValue;
+             };
+             rows: {
+               email: {
+                 googleAttribute: LocalizationValue;
+                 appAttribute: LocalizationValue;
+               };
+               firstName: {
+                 googleAttribute: LocalizationValue;
+                 appAttribute: LocalizationValue;
+               };
+               lastName: {
+                 googleAttribute: LocalizationValue;
+                 appAttribute: LocalizationValue;
+               };
              };
+           };
+         };
+         configureUserAccess: {
+           headerSubtitle: LocalizationValue;
+           assignUsersInstructions: {
+             paragraph1: LocalizationValue;
+             step1: LocalizationValue;
+             step2: LocalizationValue;
+             step3: LocalizationValue;
+             paragraph2: LocalizationValue;
            };
          };
        };
// ... 182 unchanged lines elided ...

Static analyzer: Breaking change in type alias __internal_LocalizationResource: Type changed: {locale:string;maintenanceMode:import("@clerk/shared").LocalizationValue;roles:{[r:string]:import("@clerk/shared").Loca…{locale:string;maintenanceMode:import("@clerk/shared").LocalizationValue;roles:{[r:string]:import("@clerk/shared").Loca…

🤖 AI review (reclassified as non-breaking) (85%): The __internal_LocalizationResource type had additions to the configureSSO.selectProviderStep.saml object (new google field) and a new configureSSO.configureStep.samlGoogle section. These are purely additive changes to a localization resource type. Since LocalizationResource is exposed as DeepPartial, consumers supplying partial localization objects are not required to provide the new fields. The type is also prefixed with __internal_ suggesting it is not meant for direct consumer use.

Modified: FieldId
- type FieldId = 'firstName' | 'lastName' | 'name' | 'slug' | 'emailAddress' | 'phoneNumber' | 'currentPassword' | 'newPassword' | 'signOutOfOtherSessions' | 'passkeyName' | 'password' | 'confirmPassword' | 'identifier' | 'username' | 'code' | 'role' | 'deleteConfirmation' | 'deleteOrganizationConfirmation' | 'enrollmentMode' | 'affiliationEmailAddress' | 'deleteExistingInvitationsSuggestions' | 'legalAccepted' | 'apiKeyDescription' | 'apiKeyExpirationDate' | 'apiKeyRevokeConfirmation' | 'apiKeySecret' | 'idpCertificate' | 'idpEntityId' | 'idpMetadataUrl' | 'idpSsoUrl' | 'acsUrl' | 'spEntityId' | 'web3WalletName';
+ type FieldId = 'firstName' | 'lastName' | 'name' | 'slug' | 'emailAddress' | 'phoneNumber' | 'currentPassword' | 'newPassword' | 'signOutOfOtherSessions' | 'passkeyName' | 'password' | 'confirmPassword' | 'identifier' | 'username' | 'code' | 'role' | 'deleteConfirmation' | 'deleteOrganizationConfirmation' | 'enrollmentMode' | 'affiliationEmailAddress' | 'deleteExistingInvitationsSuggestions' | 'legalAccepted' | 'apiKeyDescription' | 'apiKeyExpirationDate' | 'apiKeyRevokeConfirmation' | 'apiKeySecret' | 'idpCertificate' | 'idpEntityId' | 'idpMetadata' | 'idpMetadataUrl' | 'idpSsoUrl' | 'acsUrl' | 'spEntityId' | 'web3WalletName';

Static analyzer: Breaking change in type alias FieldId: Type changed: 'firstName'|'lastName'|'name'|'slug'|'emailAddress'|'phoneNumber'|'currentPassword'|'newPassword'|'signOutOfOtherSessio…'firstName'|'lastName'|'name'|'slug'|'emailAddress'|'phoneNumber'|'currentPassword'|'newPassword'|'signOutOfOtherSessio…

🤖 AI review (reclassified as non-breaking) (80%): The FieldId union type gained a new member 'idpMetadata'. Adding a new member to a union type used as a field identifier is additive — existing consumer code that uses known FieldId values is unaffected, and consumers are not required to handle all union members exhaustively in well-typed code unless they have an exhaustive switch, which would be unusual for this UI-facing type.

Subpath ./url-CcPzUbGM

🟢 Additions (1)

Added: ./url-CcPzUbGM

New subpath export ./url-CcPzUbGM (24 exported members)

Subpath ./url-Dvy3tJz6

🔴 Breaking Changes (1)

Changed: ./url-Dvy3tJz6

Subpath export ./url-Dvy3tJz6 was removed


Report generated by Break Check

Comment thread packages/localizations/src/en-US.ts Outdated
Comment thread packages/localizations/src/en-US.ts Outdated
Comment thread packages/localizations/src/en-US.ts Outdated
Comment thread packages/localizations/src/en-US.ts Outdated
Comment on lines +102 to +111
<Col
elementDescriptor={descriptors.configureSSOInstructionsList}
as='ul'
sx={theme => ({
gap: theme.space.$1x5,
margin: 0,
paddingInlineStart: theme.space.$5,
listStyleType: 'disc',
})}
>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have been repeating this block here, maybe we could abstract to a shared Ul component.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will chat with Alex to see if we could create a Ul primitive on clerk/ui

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree it's turning out repetitive!

@LauraBeatris LauraBeatris force-pushed the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch from 0b0378b to 3ba2066 Compare June 1, 2026 19:41
@brkalow brkalow merged commit afb75e6 into main Jun 1, 2026
44 of 45 checks passed
@brkalow brkalow deleted the laura/orgs-1533-sdk-add-support-for-google-workspace-saml branch June 1, 2026 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants