Skip to content

Add OIDC trusted publishing + staged npm releases via GitHub Actions#363

Open
patocallaghan wants to merge 4 commits into
masterfrom
patoc/oidc-staged-publishing
Open

Add OIDC trusted publishing + staged npm releases via GitHub Actions#363
patocallaghan wants to merge 4 commits into
masterfrom
patoc/oidc-staged-publishing

Conversation

@patocallaghan

Copy link
Copy Markdown
Member

Why?

Moves npm publishing onto short-lived, per-run OIDC credentials with a human approval step, removing the need for a stored long-lived npm token.

How?

Adds a release-triggered GitHub Actions workflow that authenticates to npm via OIDC (no token) and uses npm's staged publishing, so each release is queued for a maintainer to approve before it goes live.

Generated with Claude Code

patocallaghan and others added 3 commits June 10, 2026 11:04
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d runtime

Apply the fixes validated on the passport-intercom and cli publish
workflows:
- verify: use fetch-depth: 0 and drop the manual `git fetch --depth=1`,
  so the default-branch ancestry check has the history it needs (the
  double-shallow version could only pass when the tag was the branch tip)
- add a top-level concurrency group so overlapping releases serialize
  instead of racing for a dist-tag
- add timeout-minutes: 15 to stage-publish

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@socket-security

socket-security Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgithub/​actions/​checkout@​de0fac2e4500dabe0009e67214ff5f5447ce83dd99100100100100

View full report

@patocallaghan patocallaghan marked this pull request as ready for review June 12, 2026 09:47
@imSzukala

Copy link
Copy Markdown
Contributor

Review

Ported the RN OIDC staged-publishing workflow (intercom/intercom-react-native#360) faithfully on the security scaffolding — SHA-pinned actions, env-var indirection for the release tag, least-privilege id-token: write on the publish job only, ancestry gate, concurrency, and staged publish all look good. But there are two blockers stemming from the fact that this repo's layout differs from the RN repo.

🔴 Blocker 1 — Workflow runs from repo root, but there is no root package.json

The publishable package (cordova-plugin-intercom) lives in intercom-plugin/; there is no root package.json. So:

  • verifynode -p "require('./package.json').version" fails immediately.
  • stage-publishnpm stage publish from root has no package to publish.

Both the version assert and the publish step need to run inside intercom-plugin/ (e.g. working-directory: ./intercom-plugin, and read ./intercom-plugin/package.json). RN's package is at the repo root, so it didn't need this — this is exactly the "editable region" the CAVEAT header flags as unresolved. As written the workflow cannot succeed.

🔴 Blocker 2 — The CircleCI publish job is not removed → double publish

circle.yml is untouched, and its publish job fires on any tag matching /[0-9]+(\.[0-9]+)+/. Publishing a GitHub Release creates that tag, so the existing CircleCI token-based publish and the new GH Actions OIDC publish both fire on every release — they race on the dist-tag, and it defeats the purpose of removing the stored NPM_TOKEN.

RN's PR deleted its release-to-npm job and its workflow wiring from CircleCI. This PR needs to remove the publish job + its workflow entry from circle.yml.

🟠 Issue 3 — Missing TOCTOU / tag-mutability hardening that RN added

RN's final commit (6d565ef) had validate emit the resolved, ancestry-checked SHA and had downstream jobs check out that SHA rather than re-resolving the mutable tag. Here, stage-publish does a fresh checkout with no ref:, re-resolving the tag independently of what verify validated — the window RN deliberately closed. Suggest mirroring RN: have verify output sha, and stage-publish check out ${{ needs.verify.outputs.sha }}.

🟡 Minor

  • No build/test job — acceptable here. The plugin ships source as-is, has no prepare/build script (package.json has no scripts), matching the old CircleCI behaviour (npm publish only). RN needed lint/ts/test/build; Cordova legitimately doesn't.
  • Prerelease dist-tag is next vs RN's beta — fine if intentional.
  • OIDC trusted-publisher config on npmjs.org must target workflow filename publish.yml and the intercom-plugin package (operational prerequisite, not in the diff).
  • The large CAVEAT header should be resolved and removed before merge rather than shipped — its presence signals the workflow is still a template.

Bottom line

Not mergeable as-is. Fix the two blockers — (1) wire every package step to intercom-plugin/, and (2) delete the CircleCI publish job so releases don't publish twice — and ideally fold in RN's SHA-pinning between jobs.

~ Automated via Claude

…sh, pin SHA between jobs

- Run version assert, dist-tag resolve, and `npm stage publish` inside
  intercom-plugin/ (the publishable package lives there; no root package.json).
- Remove the CircleCI token-based publish job and its workflow wiring so
  releases no longer double-publish / race on the dist-tag.
- verify now outputs the ancestry-checked SHA; stage-publish checks out that
  exact SHA instead of re-resolving the mutable tag (closes the TOCTOU window).
- Remove the resolved CAVEAT template header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@patocallaghan

Copy link
Copy Markdown
Member Author

@imSzukala Thanks for the review. I've updated per your feedback 🙇‍♂️

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