Skip to content

feat(wizard): auto-register OIDC trusted publisher during apply#242

Merged
vpetersson merged 5 commits into
sbomify:feat/wizard-and-shared-api-clientfrom
aurangzaib048:feat/oidc-auto-register
Jun 2, 2026
Merged

feat(wizard): auto-register OIDC trusted publisher during apply#242
vpetersson merged 5 commits into
sbomify:feat/wizard-and-shared-api-clientfrom
aurangzaib048:feat/oidc-auto-register

Conversation

@aurangzaib048
Copy link
Copy Markdown
Contributor

@aurangzaib048 aurangzaib048 commented Jun 1, 2026

Summary

The onboarding wizard emits an OIDC workflow but the trust binding still had to be created by hand in the sbomify UI — the #1 reason a first OIDC publish 403s. This registers the binding automatically during apply, for public and private repos, sending only org/repo — no GitHub token.

Base: feat/wizard-and-shared-api-client (wizard branch, PR #238).
Depends on sbomify/sbomify#989 (deferred ID pinning). The wizard sends just the repo name; the backend resolves the immutable IDs for public repos at create time and defers to the first OIDC publish for private repos (pin-on-first-use). No GitHub token is ever involved.

What changed

  • SbomifyApiClient.create_oidc_binding(component_id, repository) — thin POST /api/v1/auth/oidc/github/bindings, name only. Idempotent (409 = already bound → success); raises on other non-2xx.
  • apply_plan — new best-effort step (OIDC mode): register a binding per applied component, by name, regardless of repo visibility. Never fatal — per-component failures are warnings; the only skip is when the git remote yields no owner/repo slug.
  • Done screen — shows ✓ OIDC trusted publishing is set up on success, else the manual instructions (with the reason). Also fixes the component link: /component/{id}/ (the old /components/{id}/settings 404'd).
  • New WizardState fields carry the outcome to the Done screen.

Testing

  • Tests: client (201/409/error, name-only body); apply (registers per component for public and private, token-mode skip, 409-counts, failure-is-a-warning, no-slug skip); Done rendering (success / fallback / corrected URL).
  • Full suite: 2329 passed. Pre-commit green (ruff, ruff-format, mypy).

History

Earlier revisions of this PR had the wizard resolve private-repo IDs via a local GitHub token; that was dropped in favour of backend deferred pinning (#989) so setup needs nothing but the repo name — identical to adding a public repo in the UI.

The wizard emits an OIDC workflow but the trust binding still had to be
created by hand in the sbomify UI — the sbomify#1 reason a first OIDC publish
403s. Register it automatically during apply instead, using the new
binding-management API.

* SbomifyApiClient.create_oidc_binding(component_id, repository) — POSTs
  to /api/v1/auth/oidc/github/bindings. Idempotent: 409 (already bound)
  is success; 400/404/5xx raise APIError.
* apply_plan: new best-effort step (oidc mode only) that registers a
  binding per applied component. Never fatal — per-component failures are
  warnings; skipped with a clear note for private repos (backend can't
  resolve private GitHub IDs yet) and when no owner/repo slug is known.
* Done screen: shows a '✓ trusted publishing is set up' panel on success,
  or falls back to the manual instructions (prefixed with the reason)
  when auto-registration was skipped/failed.

12 new tests (client 201/409/error, apply per-component/token-skip/
409-counts/failure-is-warning/private-skip/no-slug, done rendering).
Full suite: 2329 passed.

Depends on the main-app binding API (sbomify/sbomify#988).
Auto-registration previously skipped private repos: sbomify can't read a
private repo's metadata anonymously, so the binding create 400'd. For a
private repo the wizard now resolves the immutable IDs itself, using the
operator's own local GitHub auth, and passes them to the binding API
(which accepts explicit IDs). The token is used to call GitHub directly
from the machine and is NEVER sent to sbomify — only the two integers are.

* repo_facts.github_token() — GH_TOKEN / GITHUB_TOKEN / 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.
* repo_facts.resolve_repo_ids(slug, token) — authenticated GET /repos/{slug}
  -> (repository_id, repository_owner_id); None on any failure.
* SbomifyApiClient.create_oidc_binding gains optional repository_id /
  repository_owner_id (sent only when both present).
* apply._register_oidc_bindings: private repo -> resolve IDs + register with
  them; no token or resolve failure -> graceful skip + note (unchanged for
  public repos, which still let the backend resolve the slug).

Verified live end-to-end against a real private repo: the wizard resolved
its IDs and created bindings carrying the correct immutable values, which a
GitHub Actions OIDC token from that repo would match at exchange time.

New tests for github_token, resolve_repo_ids, the client body, and the
apply paths (private+token, private+no-token, private+resolve-fail, public).
Full suite: 2342 passed.
…not /components/{id}/settings)

The Done screen's OIDC manual-fallback links and the 'copy URL' action
pointed at `/components/{id}/settings`, which 404s — the real component
page (carrying the Trusted Publishing section) is `/component/{id}/`
(singular, no /settings). Surfaced by the private-repo E2E. Updated the
done test to assert the correct path and guard against the old one.
…en logic

Per the converged design (backend now pins private-repo IDs on first OIDC
publish), the wizard no longer needs to resolve repository IDs itself. It just
sends 'org/repo' — same call for public and private repos, no GitHub token.

* Remove repo_facts.github_token() + resolve_repo_ids() (and the os/shutil
  imports + their tests).
* create_oidc_binding: name only (drop repository_id/repository_owner_id).
* apply._register_oidc_bindings: register by name for every component
  regardless of visibility; only skip when the git remote yields no
  'owner/repo' slug. No token, no per-visibility branching.

Keeps the Done-screen component-URL fix. Full suite: 2329 passed.
Depends on sbomify/sbomify#989 (deferred pinning).
Commit 5de2281 swept in untracked local scratch (docs/plans/, .agent/,
audit_trail.txt, and assorted *.cdx.json/*.spdx.json SBOM outputs) that don't
belong in the repo. Untrack them (left on disk locally). The OIDC code/test
changes from that commit are unaffected.
@vpetersson vpetersson merged commit 7543942 into sbomify:feat/wizard-and-shared-api-client Jun 2, 2026
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