Skip to content

Add a B2B organizations section to the staff dashboard (staff UI 4/4) - #3997

Open
blarghmatey wants to merge 3 commits into
staff-dashboard-refine4-routesfrom
b2b-staff-ui-orgs-sso
Open

blarghmatey wants to merge 3 commits into
staff-dashboard-refine4-routesfrom
b2b-staff-ui-orgs-sso

Conversation

@blarghmatey

@blarghmatey blarghmatey commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

What are the relevant tickets?

Phase 2 of the B2B onboarding RFC, https://github.com/mitodl/hq/discussions/12784: the staff UI for orgs and SSO over C1. We retire the per-org Pulumi path once we're confident provisioning through this UI works.

Stack (4 of 4). Based on 3/4. It uses the list, SP details and change history that 1/4 adds to the provisioning API.

Description (What does it do?)

Today, onboarding a partner's organization and SSO means a reviewed Pulumi PR against olapps.py, and the C1 provisioning API has no operator surface. This adds a B2B Organizations section to the staff dashboard for CS/ops.

  • Organizations: a list filterable by name, org key and onboarding state. Each row shows the onboarding state and every IdP's lifecycle state. There are also create and edit pages. Edit sends only the fields that changed. Keycloak can hold a null redirect URL, which the update endpoint rejects, and any domains sent get rewritten as verified. org_key is only sent on create. An org without SSO is a normal case and shows an empty IdP table.
  • Onboarding: the current state, when it changed, and a form to record a new state with notes. It's descriptive, and nothing gates on it.
  • Identity providers:
    • Add a SAML IdP (metadata URL or pasted XML) or an OIDC IdP (discovery URL, client ID and secret). SAML mappings can match an attribute by FriendlyName or by Name (attribute_map / attribute_name_map).
    • "Check what Keycloak reads from this metadata" calls parse-metadata and shows the parsed config before anything is created.
    • Each IdP has its allowed lifecycle moves (draft, testing, active, disabled) behind a confirmation, plus metadata refresh and delete.
    • Each IdP shows the SP entity ID, ACS URL and SP metadata URL to hand to the partner.
  • Change history: the provisioning audit events from 1/4, newest first, with who made each change and the before and after data.

The section is open to is_staff users, not only superusers, which matches the API's IsAdminUser. Per the 2026-09-18 decision, there's no approval step before an IdP goes active. The change history is the control.

Not in this PR:

How can this be tested?

tsc --noEmit is clean and the production build passes. I drove the section in headless Chromium against an in-memory mock of 1/4's API shapes. No page errors, and the request bodies matched the serializers. The flows:

  • list, search and filter (?l=25&o=0&q=...)
  • the org page, with SP details expanded
  • saving an onboarding state (the CSRF header was sent) and seeing it in the change history
  • moving an IdP from testing to active through the confirmation
  • the metadata preview, then creating a SAML IdP with FriendlyName and Name mappings
  • creating an OIDC IdP, which sends no SAML mappings
  • creating an org, which lands on its page
  • editing: an untouched save of an org with a null redirect URL sends no request, and a description change sends only description

Not yet run against a real backend and Keycloak. In the QA environment, as a staff user:

  • Create an org under /staff-dashboard/b2b_organizations.
  • Add an IdP from a partner's (or a test IdP's) metadata and move it to Testing.
  • Check that the org and IdP appear in the QA realm, and that each step shows up in Change history.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VcbiSRu3CsE4uyfr6Lqjdr

@blarghmatey
blarghmatey added this pull request to stack #3998 September 19, 2026 01:28
@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

OpenAPI Changes

Show/hide changes
## Changes for v0.yaml:
No changes detected

## Changes for v1.yaml:
No changes detected

## Changes for v2.yaml:
No changes detected

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@blarghmatey blarghmatey changed the title Add a B2B organizations section to the staff dashboard Add a B2B organizations section to the staff dashboard (staff UI 4/4) Sep 19, 2026
@blarghmatey
blarghmatey marked this pull request as ready for review September 19, 2026 01:30
@blarghmatey
blarghmatey requested a balanced review from Copilot September 19, 2026 01:32

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

Valid pasted XML can be rendered incorrectly, and the mapping removal control is inaccessible by keyboard.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds staff-dashboard workflows for managing B2B organizations, onboarding, identity providers, and provisioning history.

Changes:

  • Adds organization list, create, edit, and detail views.
  • Adds SAML/OIDC provisioning and lifecycle controls.
  • Adds onboarding state and audit-history interfaces.
File Description
App.tsx Registers routes, resources, and staff access.
interfaces/​index.d.ts Defines provisioning API types.
components/​b2b/​constants.ts Defines states, transitions, and resources.
components/​b2b/​identity_providers.tsx Displays and manages identity providers.
components/​b2b/​onboarding_card.tsx Manages onboarding state.
components/​b2b/​organization_form.tsx Provides shared organization fields.
components/​b2b/​provisioning_events.tsx Displays provisioning history.
components/​b2b/​use_refresh_organization.ts Invalidates provisioning queries.
pages/​b2b_organizations/​create.tsx Adds organization creation.
pages/​b2b_organizations/​edit.tsx Adds minimal organization updates.
pages/​b2b_organizations/​identity_provider_create.tsx Adds SAML/OIDC creation workflow.
pages/​b2b_organizations/​index.ts Exports organization pages.
pages/​b2b_organizations/​list.tsx Adds searchable organization listing.
pages/​b2b_organizations/​show.tsx Adds organization detail view.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread frontend/staff-dashboard/src/components/b2b/identity_providers.tsx Outdated
Comment thread frontend/staff-dashboard/src/pages/b2b_organizations/identity_provider_create.tsx Outdated
Comment thread frontend/staff-dashboard/src/App.tsx
Comment thread frontend/staff-dashboard/src/components/b2b/identity_providers.tsx Outdated
Comment thread frontend/staff-dashboard/src/pages/b2b_organizations/edit.tsx
blarghmatey and others added 3 commits September 25, 2026 15:39
Onboarding a B2B partner's organization and SSO today means a reviewed
Pulumi PR against ol-infrastructure, and the C1 provisioning API has had no
operator surface. This adds one for CS/ops staff, over the provisioning
routes and the list, SP details and change history added earlier in this
stack.

- Organizations: a filterable list with onboarding state and each IdP's
  lifecycle state, create, and edit. Edit sends only the fields that
  changed: Keycloak can hold a null redirect URL, which the update
  endpoint rejects, and any domains sent are rewritten as verified.
  org_key is only sent on create. An organization without SSO is a normal
  case and shows an empty IdP table.
- Onboarding: the current state, when it changed, and a form to record a
  new state with notes. Nothing gates on it.
- Identity providers: add a SAML (metadata URL or pasted XML) or OIDC
  (discovery URL and client credentials) IdP. SAML mappings match an
  attribute by FriendlyName or by Name. "Check what Keycloak reads" calls
  parse-metadata and shows the parsed config before anything is created.
  Each IdP has the allowed lifecycle moves (draft, testing, active,
  disabled) behind a confirmation, metadata refresh, delete, and the SP
  entity ID, ACS URL and SP metadata URL to hand to the partner.
- Change history: the provisioning audit events, newest first, with the
  before and after data.

The section is open to is_staff users, not only superusers, matching the
API's IsAdminUser permission.

There is no test-login button yet. Nothing in MITx Online passes
kc_idp_hint through to Keycloak today, so moving an IdP to testing still
needs an operator to build that login URL by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcbiSRu3CsE4uyfr6Lqjdr
…emoval a button

A pasted metadata document can start with whitespace, which the backend
strips before detecting XML, so the IdP details showed the whole document
as its source. The remove-mapping control was a bare icon that keyboard
users could not reach; it is now a labelled button.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcbiSRu3CsE4uyfr6Lqjdr
lifecycle_state comes from the API, so a state added on the backend before
IDP_ALLOWED_TRANSITIONS learns it would crash the organization page on
.map of undefined. It now renders with "Move to" disabled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcbiSRu3CsE4uyfr6Lqjdr

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants