feat(SCIM): SCIM configuration management UI#7528
Conversation
Adds the frontend UI consuming the backend SCIM management endpoints from #7512. The existing SAML tab in Organisation Settings is renamed to SSO and now hosts both SAML and SCIM as stacked sections gated by the same Enterprise plan check. Closes #7151. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| key: 'saml', | ||
| label: 'SAML', | ||
| key: 'sso', | ||
| label: 'SSO', |
There was a problem hiding this comment.
Can we redirect from ?tab=saml to ?tab=sso?
|
|
||
| export const SSOTab = ({ organisationId }: SSOTabProps) => { | ||
| return ( | ||
| <PlanBasedBanner feature='SAML' theme='page'> |
There was a problem hiding this comment.
This is gated on SAML feature, so the SCIM entry in featureDescriptions isn't really used.
There was a problem hiding this comment.
Good call. Add two independent gates in 61aff57 so we can point the user to the right doc.
| import Button from './base/forms/Button' | ||
| import EmptyState from './EmptyState' | ||
| import Icon from './icons/Icon' | ||
| import Loader from './Loader' |
There was a problem hiding this comment.
Replaced with a Skeleton 166700e. Loader import gone.
| 'SCIM': { | ||
| description: | ||
| 'Provision and de-provision users and groups automatically from your identity provider.', | ||
| docs: 'https://docs.flagsmith.com/system-administration/authentication/', |
There was a problem hiding this comment.
As per #7139:
| docs: 'https://docs.flagsmith.com/system-administration/authentication/', | |
| docs: 'https://docs.flagsmith.com/administration-and-security/access-control/scim/', |
- Loading state: replace the spinner with a shimmer Skeleton that
mirrors the configured layout, so the page doesn't jump when data
arrives.
- Token modal: warning banner uses the shared WarningMessage component;
token field renders monospace; Done now triggers an in-modal "Have
you saved the token?" confirmation row (avoids a stacked-modal bug
where openConfirm clobbers global.closeModal). Fade-in transition on
the confirmation row, respects prefers-reduced-motion.
- New CopyField component for the recurring read-only input + Copy
button pattern. Used here for the SCIM base URL and the token field;
follow-up PR will migrate the other consumers (CreateSAML's ACS URL,
SDK keys, etc.).
- Design-system: drop hardcoded fill='#fff' on the trash icon, drop the
copied-from-CreateSAML 'create-feature-tab' wrapper class, drop the
inputProps={{readOnly: true}} pattern (rendered as [object Object] in
the DOM via React's unknown-attribute pass-through).
- Copy: empty-state CTA reads "Create a SCIM configuration" to match
SAML's pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Existing bookmarks and links to the renamed SAML tab now land on the SSO tab automatically instead of falling back to the default tab. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each section now has its own PlanBasedBanner so the upgrade prompt shown to non-Enterprise users carries the feature-specific copy and docs URL. Previously SCIM lived under SAML's gate and the SCIM entry in featureDescriptions was unreachable — addressed by: - Splitting SSOTab into two banners (feature='SAML' and feature='SCIM') inside a flex column with breathing room between them. - Pointing the SCIM docs link at the SCIM-specific docs page per #7139. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous wrapper used `gap-4 mt-4` on a flex column, which doubled
up with the SamlTab/ScimSection internal `mt-4 mb-4` wrappers when the
user has access (PlanBasedBanner returns `<>{children}</>` and the
spacing on the outer div was still in play). Moving the spacing to
`className` on each banner means it only applies on the upgrade-prompt
path — the configured path is unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The scim-token-modal.scss file is only used by ScimTokenModal — moving it from the global components/ partial folder next to the .tsx and importing it directly. Matches the pattern used by other component- local SCSS (e.g. LinkedExperimentSection.scss). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Delete `pages/organisation-settings/tabs/SAMLTab.tsx`: dead since `SSOTab` replaced it; nothing references it any more. - Drop the `_scim-token-modal.scss` animation; the row already appears on user-initiated click, the fade-in wasn't load-bearing. - Remove the `fs-caption` class — it doesn't exist in `web/styles/` and was silently a no-op (visual unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`fs-caption` doesn't exist in `web/styles/`, was silently no-op (visual unchanged). Also drops the dead `scim-confirm-row` class and its `.scss` import after the animation was removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
Docker builds report
|
`Icon.tsx` already falls back to `currentColor` when no `fill` is passed (Icon.tsx:104), which makes the icon inherit the button's text colour and adapt to dark mode automatically. The hardcoded grey was invisible in dark contexts. Covers the three icons in the SSO surface we now own: - SamlSection: trash on row delete button - SAMLAttributeMappingTable: trash on attribute delete button - CreateSAML: copy on ACS URL button This aligns with the design-system audit's top finding (issue #6606). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same pattern as `general-tab/index.tsx` and the parent `sso/index.tsx` — each feature folder owns one entry point at `index.tsx`. Lets the parent import as `from './saml'` and `from './scim'` instead of naming the file twice in the path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover the variants `CopyField` is rendered in across the codebase: default URL, monospace token (the SCIM bearer token use case), short identifier, and a long URL that exercises overflow behaviour. Story file mirrors `GhostInput.stories.tsx` — `Components/Forms/` category, no controls playground, one `render` per variant so Chromatic snapshots each one rather than just the default args. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Playwright Test Results (oss - depot-ubuntu-latest-16)Details
Playwright Test Results (private-cloud - depot-ubuntu-latest-16)Details
Playwright Test Results (oss - depot-ubuntu-latest-arm-16)Details
Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)Details
Playwright Test Results (oss - depot-ubuntu-latest-16)Details
Playwright Test Results (oss - depot-ubuntu-latest-arm-16)Details
Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)Details
Playwright Test Results (private-cloud - depot-ubuntu-latest-16)Details
Playwright Test Results (oss - depot-ubuntu-latest-arm-16)Details
Playwright Test Results (oss - depot-ubuntu-latest-16)Details
Playwright Test Results (private-cloud - depot-ubuntu-latest-16)Details
Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)Details
|
Visual Regression19 screenshots compared. See report for details. |
React infers `displayName` from the variable name when a component is declared as a named const FC. Setting it explicitly on `CopyField`, `ScimSection`, and `ScimTokenModal` was boilerplate that added no value — DevTools and React error messages already showed the right name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The main app's bundle injects Flex/Row as globals via `project-components.js`, but Storybook only re-registers a safe subset (preview.js:55-57). `Flex` is deliberately skipped — the preview's comment notes it crashed under the legacy JSX transform when it was still a `.js` file (it's `.tsx` now, but the global was never restored). Chromatic surfaced this as `ReferenceError: Flex is not defined` on the CopyField:Default story. Importing both explicitly makes the component self-contained and works in any build context. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Thanks for submitting a PR! Please check the boxes below:
docs/if required so people know about the feature.Changes
Closes #7151.
Adds the frontend for the SCIM configuration endpoints from #7512. The SAML tab in Organisation Settings is renamed to SSO and now holds both SAML and SCIM as stacked sections behind a single Enterprise gate.
Screenshots
What it does
useScimConfigurationservice (get / create / delete / regenerate-token) scoped per organisation, cache-tagged for invalidation.ScimSectioncovers loading, empty (404), and configured states. Loading is a shimmer skeleton that mirrors the configured layout so the page doesn't jump when data arrives.ScimTokenModalshows the bearer token once on create or regeneration. It comes straight from the mutation result, never gets cached, and is gone from memory when the modal closes. The Done action triggers an in-modal "Have you saved the token?" step before closing — see the known limitation below.SSOTabis a thin container that wraps both sections inPlanBasedBanner feature='SAML'. SAML's previous internal banner moved up so we don't render duplicate upgrade prompts.?tab=samlto?tab=sso. Old bookmarks land on the default tab.The
base_urlis server-derived (built viarequest.build_absolute_uri(reverse("scim:root"))inflagsmith-private) so we just display what comes back — no client-side URL building like SAML's ACS URL.CopyField
A few places across the app roll the same "read-only input + Copy button" pattern inline (CreateSAML's ACS URL, SDK keys, server-side keys, webhook URLs, etc.). I added a small
CopyFieldhere and used it for both the SCIM base URL and the token. The API is deliberately minimal (value,className,data-test) so a follow-up PR can migrate the other consumers without painting us into a corner.Known limitation
The modal's X close button bypasses the inline "Have you saved the token?" step. Same trade-off
AdminAPIKeyshas — the proper fix sits at thewithModallayer, which clobbersglobal.closeModalwhen_Confirmmounts and never restores it. Out of scope here.How did you test this?
Manual end-to-end on a local environment with
--extra scim:token_rotated_atupdates after close.?tab=ssoopens the SSO tab; old?tab=samllands on the default tab.Plus
npm run typecheckandnpx eslint --fixon modified files. No new errors.