From ace663e9727ef84df808570d90cde75c119064e6 Mon Sep 17 00:00:00 2001 From: Farhan Anjum Date: Mon, 20 Jul 2026 20:51:36 +0600 Subject: [PATCH] [FSSDK-12891] Switch npm publishing to OIDC trusted publishing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace long-lived PUBLISH_REACT_TO_NPM_FROM_GITHUB token with OIDC trusted publishing for more secure, tokenless CI publishing to npm. Workflow changes: - Add id-token: write permission for OIDC token generation - Move permissions to workflow level (add packages: write for GHR) - Add GitHub environment gate (npm) for deployment protection - Upgrade Node to 22, add npm upgrade step (OIDC requires npm 11.5.1+) - Remove registry-url from setup-node (prevents .npmrc token entry that would conflict with OIDC) - Remove PUBLISH_REACT_TO_NPM_FROM_GITHUB from npm publish step - Add workflow_dispatch trigger for dry-run testing - Add DRY_RUN env to both publish steps publish.sh changes: - Make NODE_AUTH_TOKEN optional in curl registry lookup (public packages don't need auth for reads) - Add --provenance flag for npm registry publishes GHR publish step unchanged — still uses GITHUB_TOKEN. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/react_release.yml | 23 ++++++++++++++++------- scripts/publish.sh | 15 ++++++++++++--- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/.github/workflows/react_release.yml b/.github/workflows/react_release.yml index db5ea57..45edf53 100644 --- a/.github/workflows/react_release.yml +++ b/.github/workflows/react_release.yml @@ -3,14 +3,19 @@ name: Publish React SDK on: release: types: [ published ] + workflow_dispatch: {} + +permissions: + id-token: write + contents: read + packages: write jobs: publish: name: Publish to NPM and GitHub Package Registry runs-on: ubuntu-latest - permissions: - contents: read - packages: write + if: ${{ github.event_name == 'workflow_dispatch' || !github.event.release.draft }} + environment: npm steps: - name: Checkout branch uses: actions/checkout@v4 @@ -20,11 +25,12 @@ jobs: - name: Set up Node uses: actions/setup-node@v4 with: - node-version: 18 - registry-url: "https://registry.npmjs.org/" - always-auth: "true" + node-version: 22 cache: 'npm' + - name: Upgrade npm for OIDC support + run: npm install -g npm@latest + - name: Configure GHR auth run: echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> ~/.npmrc @@ -39,14 +45,17 @@ jobs: tarball=$(npm pack --ignore-scripts | tail -n1) echo "tarball=$tarball" >> "$GITHUB_OUTPUT" + # npm publish uses OIDC trusted publishing (no token needed). + # publish.sh adds --provenance for supply-chain attestations. - name: Publish to NPM env: - NODE_AUTH_TOKEN: ${{ secrets.PUBLISH_REACT_TO_NPM_FROM_GITHUB }} TARBALL: ${{ steps.pack.outputs.tarball }} + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }} run: scripts/publish.sh "https://registry.npmjs.org" "$TARBALL" - name: Publish to GitHub Package Registry env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TARBALL: ${{ steps.pack.outputs.tarball }} + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }} run: scripts/publish.sh "https://npm.pkg.github.com" "$TARBALL" diff --git a/scripts/publish.sh b/scripts/publish.sh index 1241c86..3429105 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -88,8 +88,12 @@ trap 'rm -f "$body_file"' EXIT # curl -sS (no --fail) returns 0 for any HTTP response, non-zero only on # network/protocol errors — so we can cleanly separate "server answered" from # "could not reach server". +auth_header="" +if [[ -n "${NODE_AUTH_TOKEN:-}" ]]; then + auth_header="Authorization: Bearer ${NODE_AUTH_TOKEN}" +fi if ! http_code=$(curl -sS -m 30 -o "$body_file" -w '%{http_code}' \ - -H "Authorization: Bearer ${NODE_AUTH_TOKEN:-}" "$packument_url"); then + ${auth_header:+-H "$auth_header"} "$packument_url"); then echo "ERROR: request to ${packument_url} failed (network/timeout); refusing to publish on an ambiguous result." >&2 exit 1 fi @@ -135,11 +139,16 @@ if [[ "$dry_run" == "true" ]]; then exit 0 fi +provenance_flag="" +if [[ "$registry" == *"registry.npmjs.org"* ]]; then + provenance_flag="--provenance" +fi + echo "Publishing ${pkg}@${version} (tag: ${tag}) to ${registry}" if [[ -n "$tarball" ]]; then # The tarball is a prebuilt artifact; skip lifecycle scripts (prepublishOnly # = test + build) that would otherwise run from the current package.json. - npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts + npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts $provenance_flag else - npm publish --registry "$registry" --tag "$tag" + npm publish --registry "$registry" --tag "$tag" $provenance_flag fi