Skip to content

feat(SCIM): SCIM configuration management UI#7528

Open
talissoncosta wants to merge 19 commits into
mainfrom
feat/scim-configuration-frontend-7151
Open

feat(SCIM): SCIM configuration management UI#7528
talissoncosta wants to merge 19 commits into
mainfrom
feat/scim-configuration-frontend-7151

Conversation

@talissoncosta
Copy link
Copy Markdown
Contributor

@talissoncosta talissoncosta commented May 15, 2026

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

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

image image

What it does

  • New useScimConfiguration service (get / create / delete / regenerate-token) scoped per organisation, cache-tagged for invalidation.
  • ScimSection covers 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.
  • ScimTokenModal shows 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.
  • SSOTab is a thin container that wraps both sections in PlanBasedBanner feature='SAML'. SAML's previous internal banner moved up so we don't render duplicate upgrade prompts.
  • Tab key migrates from ?tab=saml to ?tab=sso. Old bookmarks land on the default tab.

The base_url is server-derived (built via request.build_absolute_uri(reverse("scim:root")) in flagsmith-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 CopyField here 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 AdminAPIKeys has — the proper fix sits at the withModal layer, which clobbers global.closeModal when _Confirm mounts and never restores it. Out of scope here.

How did you test this?

Manual end-to-end on a local environment with --extra scim:

  1. Non-Enterprise org: upgrade prompt only.
  2. Enterprise org with no SCIM config: SAML section + SCIM empty state with the Create button.
  3. Create: token modal shows the monospace token + warning. Copy works. Done triggers the inline confirmation. After confirming, the section renders the configured state.
  4. Refresh: token modal does not reappear; configured state persists.
  5. Regenerate: confirm dialog → new token in the modal → token_rotated_at updates after close.
  6. Delete: confirm (destructive) → empty state in place (RTK Query invalidation + 404 from refetch).
  7. URL routing: ?tab=sso opens the SSO tab; old ?tab=saml lands on the default tab.

Plus npm run typecheck and npx eslint --fix on modified files. No new errors.

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>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

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

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview, Comment May 18, 2026 6:21pm
flagsmith-frontend-staging Ready Ready Preview, Comment May 18, 2026 6:21pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview May 18, 2026 6:21pm

Request Review

Copy link
Copy Markdown
Member

@khvn26 khvn26 left a comment

Choose a reason for hiding this comment

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

All in all, LGTM — cross-checked with the docs and added a handful of minor comments.

Would you like to cover the happy path with an @enterprise Playwright test?

Comment on lines -116 to +117
key: 'saml',
label: 'SAML',
key: 'sso',
label: 'SSO',
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.

Can we redirect from ?tab=saml to ?tab=sso?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added in 5c65965.


export const SSOTab = ({ organisationId }: SSOTabProps) => {
return (
<PlanBasedBanner feature='SAML' theme='page'>
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.

This is gated on SAML feature, so the SCIM entry in featureDescriptions isn't really used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. Add two independent gates in 61aff57 so we can point the user to the right doc.

Comment thread frontend/web/components/ScimSection.tsx Outdated
import Button from './base/forms/Button'
import EmptyState from './EmptyState'
import Icon from './icons/Icon'
import Loader from './Loader'
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.

Isn't Loader a global?

Copy link
Copy Markdown
Contributor Author

@talissoncosta talissoncosta May 18, 2026

Choose a reason for hiding this comment

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

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/',
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.

As per #7139:

Suggested change
docs: 'https://docs.flagsmith.com/system-administration/authentication/',
docs: 'https://docs.flagsmith.com/administration-and-security/access-control/scim/',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated in 61aff57

talissoncosta and others added 2 commits May 18, 2026 13:23
- 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>
talissoncosta and others added 2 commits May 18, 2026 14:29
- 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>
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels May 18, 2026
@talissoncosta talissoncosta marked this pull request as ready for review May 18, 2026 18:01
@talissoncosta talissoncosta requested a review from a team as a code owner May 18, 2026 18:01
@talissoncosta talissoncosta requested review from kyle-ssg and removed request for a team May 18, 2026 18:01
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

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.

@talissoncosta talissoncosta requested review from Zaimwa9 and removed request for kyle-ssg May 18, 2026 18:01
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels May 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-7528 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-7528 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api:pr-7528 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-7528 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-7528 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-frontend:pr-7528 Finished ✅ Results

talissoncosta and others added 3 commits May 18, 2026 15:04
`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>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  39 seconds
commit  10252af
info  🔄 Run: #16802 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  33 seconds
commit  10252af
info  🔄 Run: #16802 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  41.5 seconds
commit  10252af
info  🔄 Run: #16802 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  57.2 seconds
commit  10252af
info  🔄 Run: #16802 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  38 seconds
commit  b39c352
info  🔄 Run: #16803 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  42.3 seconds
commit  b39c352
info  🔄 Run: #16803 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  43.3 seconds
commit  b39c352
info  🔄 Run: #16803 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 4 suites
duration  53.6 seconds
commit  b39c352
info  🔄 Run: #16803 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  43 seconds
commit  6205e6c
info  🔄 Run: #16804 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  33.8 seconds
commit  6205e6c
info  🔄 Run: #16804 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  35.3 seconds
commit  6205e6c
info  🔄 Run: #16804 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  1 minute, 13 seconds
commit  6205e6c
info  🔄 Run: #16804 (attempt 1)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

Visual Regression

19 screenshots compared. See report for details.
View full report

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>
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels May 18, 2026
@talissoncosta talissoncosta changed the title feat(SCIM): Frontend for SCIM configuration management feat(SCIM): SCIM configuration management UI May 18, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frontend for SCIM configuration management

2 participants