Skip to content

feat(OIDC): API Access tab with trust relationships UI#8044

Open
khvn26 wants to merge 5 commits into
feat/oidc-token-exchangefrom
feat/trust-relationships-ui
Open

feat(OIDC): API Access tab with trust relationships UI#8044
khvn26 wants to merge 5 commits into
feat/oidc-token-exchangefrom
feat/trust-relationships-ui

Conversation

@khvn26

@khvn26 khvn26 commented Jul 19, 2026

Copy link
Copy Markdown
Member

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

Contributes to #8040

In this PR, we add a Trust relationships management UI.

The organisation settings "API Keys" tab is renamed to "API Access" and gains a Trust Relationships section, gated by the trust_relationships feature flag.

Tabs now pass their stable key as the slug, and the label-derived legacy slug ?tab=api-keys redirects, like ?tab=saml already did.

Creation is preset-based: a provider chooser offers "GitHub Actions" or a freeform "Other OIDC provider", kept for other providers and self-hosted issuers. The GitHub form asks for a repository, picked from the org's GitHub integration if configured, or typed manually otherwise, plus an optional environment. The audience is auto-derived: https://github.com/<owner> (GitHub's zero-config default) for the first trust relationship per owner, a repo-scoped value once the default is taken.

A YAML snippet for Flagsmith/setup-cli action is presented in create/edit modals for GitHub trust relationships.

The "Is Admin" toggle is now accompanied by "available in enterprise" banner both for Master API Keys and Trust relationships.

Built in the modern RTK style (new useTrustRelationship service, typed requests/responses) rather than extending the legacy AdminAPIKeys.js. Also fixes two latent types surfaced by typed usage: createRoleMasterApiKey was missing its body, and the deleteMasterAPIKeyWithMasterAPIKeyRoles helper was annotated with the wrong request type.

How did you test this code?

Locally, by hand; no new E2E tests yet.

Screenshots

Trust relationships list:
image

New trust relationship provider chooser:
image

GitHub modal:
image

GitHub setup snippet:
image

Generic provider modal:
image

@khvn26
khvn26 requested a review from a team as a code owner July 19, 2026 16:50
@khvn26
khvn26 requested review from kyle-ssg and removed request for a team July 19, 2026 16:50
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

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

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

Request Review

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b0c30e52-4d89-4429-b4e3-b0e5abad8d9c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added the front-end Issue related to the React Front End Dashboard label Jul 19, 2026
@khvn26 khvn26 changed the title feat(trust-relationships): Add API Access tab with trust relationships UI feat(OIDC): Add API Access tab with trust relationships UI Jul 19, 2026
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

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

@github-actions github-actions Bot added the feature New feature or request label Jul 19, 2026
@khvn26
khvn26 force-pushed the feat/trust-relationships-ui branch from f52d48d to 897e6b5 Compare July 19, 2026 16:54
@khvn26
khvn26 force-pushed the feat/oidc-token-exchange branch from a9bf1ff to 5478a2a Compare July 19, 2026 16:54
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 19, 2026
@khvn26
khvn26 force-pushed the feat/oidc-token-exchange branch from 5478a2a to b88bd0c Compare July 19, 2026 17:00
@khvn26
khvn26 force-pushed the feat/trust-relationships-ui branch from 897e6b5 to ac0eb55 Compare July 19, 2026 17:00
@github-actions

This comment was marked as duplicate.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

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

@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 20, 2026
@khvn26
khvn26 force-pushed the feat/oidc-token-exchange branch from 11cd374 to 2d8d889 Compare July 20, 2026 10:23
@khvn26
khvn26 force-pushed the feat/trust-relationships-ui branch from 83c6bd9 to 8a8c79c Compare July 20, 2026 10:23
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 20, 2026
@khvn26
khvn26 force-pushed the feat/trust-relationships-ui branch from 8a8c79c to 153457d Compare July 20, 2026 10:38
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 20, 2026
@github-actions github-actions Bot added the docs Documentation updates label Jul 20, 2026
@github-actions github-actions Bot removed feature New feature or request docs Documentation updates labels Jul 20, 2026
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  41.6 seconds
commit  7dd2cf6
info  🔄 Run: #18515 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  6.3 seconds
commit  7dd2cf6
info  🔄 Run: #18515 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  6.9 seconds
commit  7dd2cf6
info  🔄 Run: #18515 (attempt 1)

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

passed  5 passed

Details

stats  5 tests across 5 suites
duration  55.8 seconds
commit  7dd2cf6
info  🔄 Run: #18515 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  37.1 seconds
commit  5b9a755
info  🔄 Run: #18516 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  44.6 seconds
commit  5b9a755
info  🔄 Run: #18516 (attempt 1)

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

passed  3 passed

Details

stats  3 tests across 3 suites
duration  58.5 seconds
commit  5b9a755
info  🔄 Run: #18516 (attempt 1)

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

passed  4 passed

Details

stats  4 tests across 4 suites
duration  36.1 seconds
commit  5b9a755
info  🔄 Run: #18516 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  40.9 seconds
commit  5d102e2
info  🔄 Run: #18518 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  46.7 seconds
commit  5d102e2
info  🔄 Run: #18518 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  33.7 seconds
commit  5d102e2
info  🔄 Run: #18518 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  58 seconds
commit  5d102e2
info  🔄 Run: #18518 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  39.9 seconds
commit  471197e
info  🔄 Run: #18521 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  45.7 seconds
commit  471197e
info  🔄 Run: #18521 (attempt 1)

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

passed  3 passed

Details

stats  3 tests across 3 suites
duration  32 seconds
commit  471197e
info  🔄 Run: #18521 (attempt 1)

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

passed  5 passed

Details

stats  5 tests across 4 suites
duration  36.3 seconds
commit  471197e
info  🔄 Run: #18521 (attempt 1)

khvn26 added 4 commits July 20, 2026 13:50
…s UI

The organisation settings API Keys tab becomes API Access and gains a
Trust Relationships section behind the trust_relationships feature flag:
list, create, edit and delete, with a claim-rules editor and the same
admin toggle and RBAC role assignment flow as master API keys, attached
via the backing key. Also fixes two latent request types in the master
API key role services.

beep boop
The GitHub/Other provider cards were div+onClick only, so keyboard and
screen-reader users could not start the create flow. Add role="button",
tabIndex and an Enter/Space onKeyDown handler.

beep boop
addRole/removeRole mutated local role state unconditionally and never
inspected the request result, so a failed assignment left the UI showing
a role the backend never stored, with no error surfaced. Update local
state only on success and toast on failure; also flag when role
assignment fails during creation instead of silently swallowing it.

beep boop
Adopt the component-folder layout the frontend guideline expects: every
new component now lives in its own directory with an index.ts barrel
(ComponentName/ComponentName.tsx + index.ts). providers.ts and errors.ts
stay as flat modules, being exempt constants/util. Cross-component
imports use the components/ alias, as the import-alias lint rule requires
for parent-traversal paths; APIKeysTab keeps importing the tab via the
unchanged ./trust-relationships path.

beep boop
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-16 — run #18626 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  55.2 seconds
commit  90be5fa
info  🔄 Run: #18626 (attempt 1)

🗂️ Previous results
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #18626 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  54.9 seconds
commit  90be5fa
info  🔄 Run: #18626 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #18626 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  38 seconds
commit  90be5fa
info  🔄 Run: #18626 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #18626 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  34.8 seconds
commit  90be5fa
info  🔄 Run: #18626 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #18528 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  54.5 seconds
commit  5a3a2d8
info  🔄 Run: #18528 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #18528 (attempt 1)

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

passed  3 passed

Details

stats  3 tests across 3 suites
duration  32.3 seconds
commit  5a3a2d8
info  🔄 Run: #18528 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #18528 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.4 seconds
commit  5a3a2d8
info  🔄 Run: #18528 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #18528 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  40.6 seconds
commit  5a3a2d8
info  🔄 Run: #18528 (attempt 1)

@khvn26

khvn26 commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

@themis-blindfold review

Comment on lines +87 to +95
const addRole = (role: SelectedRole) => {
if (isEdit) {
createRoleMasterApiKey(getStore(), {
body: { master_api_key: trustRelationship.master_api_key_id },
org_id: organisationId,
role_id: role.id,
}).then(() => toast('Role assigned'))
}
setRoles((selected) => [...selected, { id: role.id, name: role.name }])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Major · ⚡ Quick win

Role add/remove and creation-flow role assignment ignore API failures — state drifts from server.

Observed: addRole (line 87) and removeRole (line 98) update local roles state unconditionally regardless of whether the API call succeeds. In addRole during edit mode, createRoleMasterApiKey fires and .then(() => toast('Role assigned')) runs on success, but setRoles on line 95 runs immediately outside the callback — even if the request fails. removeRole has the same shape. No .catch or res.error check exists.

Observed: the creation flow (lines 146–168) calls Promise.all over createRoleMasterApiKey for each role, then unconditionally toasts 'Trust relationship created' without inspecting res.error on the results.

The sibling GithubTrustRelationshipForm.tsx already has the correct pattern (lines 139–176): it checks res.error, shows a danger toast on failure, and only calls setRoles inside the success branch. The creation flow there checks results.some((res) => res.error) and warns the user about partial failures.

Predicted: if a role assignment API call returns an error (permissions, network, deleted role), the UI will show the role as assigned/removed while the server state is different, with no feedback to the user.

Fix direction: apply the same res.error-checking pattern from GithubTrustRelationshipForm to addRole, removeRole, and the creation-flow .then() in this file.

Suggested change
const addRole = (role: SelectedRole) => {
if (isEdit) {
createRoleMasterApiKey(getStore(), {
body: { master_api_key: trustRelationship.master_api_key_id },
org_id: organisationId,
role_id: role.id,
}).then(() => toast('Role assigned'))
}
setRoles((selected) => [...selected, { id: role.id, name: role.name }])
const addRole = (role: SelectedRole) => {
if (isEdit) {
createRoleMasterApiKey(getStore(), {
body: { master_api_key: trustRelationship.master_api_key_id },
org_id: organisationId,
role_id: role.id,
}).then((res) => {
if (res.error) {
toast('Could not assign role', 'danger')
return
}
setRoles((selected) => [...selected, { id: role.id, name: role.name }])
toast('Role assigned')
})
return
}
setRoles((selected) => [...selected, { id: role.id, name: role.name }])
}

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.

Good spot, hopefully solved in 90be5fa.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Confirmed — 90be5fa addresses all three issues raised here:

  1. addRole / removeRole: The shared useTrustRelationshipRoles hook (hooks/useTrustRelationshipRoles.ts:27-81) now checks res.error before updating local state and shows a 'danger' toast on failure, matching the pattern the review suggested.
  2. Creation flow: assignRoles (hooks/useTrustRelationshipRoles.ts:46-63) returns a boolean indicating whether all assignments succeeded, and the modal (TrustRelationshipModal.tsx:113-120) now surfaces partial failures with a distinct danger toast.
  3. Drift prevention: Both TrustRelationshipModal and GithubTrustRelationshipForm now consume the same useTrustRelationshipRoles hook, so the two forms can't diverge again. The core logic is also extracted into createRoleHandlers for unit testing.

Looks good to resolve.

@themis-blindfold

Copy link
Copy Markdown

⚖️ Themis judgement: 🟠 Fix before merge

Solid new feature: a trust-relationships management UI with a well-scoped RTK Query service, typed requests/responses, a GitHub-specific preset form, a generic OIDC form, and tab plumbing. The GitHub form's role error handling was fixed in earlier review rounds, but the same fix was never applied to its sibling TrustRelationshipModal, leaving optimistic state updates that ignore API failures there. CI is green.

🎯 Correctness 3/5
🧪 Test coverage 2/5
📐 Code quality 4/5
🚀 Product impact 4/5

🟠 Majors

  • TrustRelationshipModal.tsx role operations ignore API failures — see inline comment at TrustRelationshipModal.tsx:87.

⚖️ Acknowledged

  • as unknown as SelectedRole double-cast in TrustRelationshipPermissionsFields.tsx — accepted by @khvn26 (out of scope; requires fixing RolesSelect's type).
  • isAdmin defaults to true for new trust relationships — accepted by @khvn26 as a product decision.
📝 Walkthrough
  • RTK Query service & types — new useTrustRelationship service with CRUD endpoints, typed request/response types for TrustRelationship and TrustRelationshipClaimRule, and a type fix for deleteMasterAPIKeyWithMasterAPIKeyRoles and createRoleMasterApiKey.
  • Tab plumbing — "API Keys" tab renamed to "API Access", tabLabelString set to key so URL slugs are stable, and legacy ?tab=api-keys redirects to ?tab=keys.
  • Trust relationships listTrustRelationships component with provider-badged issuer column, creation/edit modals, and delete confirmation.
  • GitHub preset formGithubTrustRelationshipForm with integration-aware repo picker, auto-derived audience, environment scoping, and a YAML setup snippet.
  • Generic OIDC formTrustRelationshipModal for freeform issuer/audience/claim-rules, with inline field-level API errors.
  • Shared permissionsTrustRelationshipPermissionsFields extracts the is-admin toggle and role selector, plus PlanBasedBanner badges added to both trust relationships and the existing Master API Keys form.
🧪 How to verify
  1. Enable the trust_relationships feature flag, open Organisation Settings > API Access, and confirm the Trust Relationships section appears below API Keys.
  2. Create a GitHub Actions trust relationship via the preset picker; verify the YAML snippet updates when the audience is non-default.
  3. Edit a generic OIDC trust relationship, add a role with RBAC disabled to trigger a 403, and verify the UI does not show the role as assigned (this is the unfixed path today).
  4. Create a generic OIDC trust relationship with roles, intentionally break one role assignment (e.g. non-existent role), and confirm the user is told about the partial failure.
  5. Navigate to ?tab=api-keys and confirm it redirects to ?tab=keys.

Automate: add a component test for TrustRelationshipModal's addRole/removeRole verifying local state only updates on API success.

Product take: This unlocks OIDC token exchange for CI pipelines — a meaningful capability for teams adopting keyless authentication. The GitHub-specific preset with auto-audience and setup snippet lowers the bar considerably compared to manual OIDC configuration.

Big picture: GithubTrustRelationshipForm and TrustRelationshipModal duplicate the role-fetching, role-add/remove, and save logic. The bug found here is a direct consequence — one copy was patched, the other wasn't. Extracting a shared useTrustRelationshipRoles hook (or similar) would eliminate the duplication and make it impossible for the two forms to drift again.

🧭 Assumptions & unverified claims
  • The review assumes Tooltip renders string children as sanitised HTML via data-tooltip-html (per the author's explanation in an earlier thread); this was not independently verified against the Tooltip component source.
  • The createRoleMasterApiKey and deleteMasterAPIKeyWithMasterAPIKeyRoles helpers are assumed to return RTK Query action results with an .error property on failure (consistent with the checked pattern in GithubTrustRelationshipForm).

A trust relationship is only as strong as its weakest modal. · reviewed at 5a3a2d8

The role add/remove fix from review only reached the GitHub preset form,
leaving the freeform modal with optimistic state updates and success
toasts on failed API calls. Role behaviour now lives in a single
useTrustRelationshipRoles hook used by both forms — including partial
failure reporting when assigning roles after creation — with its core
extracted for unit tests, so the two forms cannot drift again.

beep boop
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.

3 participants