Skip to content

Commit 3643138

Browse files
[FSSDK-12891] Switch npm publishing to OIDC trusted publishing (#345)
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) <noreply@anthropic.com>
1 parent 4b546c3 commit 3643138

2 files changed

Lines changed: 28 additions & 10 deletions

File tree

‎.github/workflows/react_release.yml‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ name: Publish React SDK
33
on:
44
release:
55
types: [ published ]
6+
workflow_dispatch: {}
7+
8+
permissions:
9+
id-token: write
10+
contents: read
11+
packages: write
612

713
jobs:
814
publish:
915
name: Publish to NPM and GitHub Package Registry
1016
runs-on: ubuntu-latest
11-
permissions:
12-
contents: read
13-
packages: write
17+
if: ${{ github.event_name == 'workflow_dispatch' || !github.event.release.draft }}
18+
environment: npm
1419
steps:
1520
- name: Checkout branch
1621
uses: actions/checkout@v4
@@ -20,11 +25,12 @@ jobs:
2025
- name: Set up Node
2126
uses: actions/setup-node@v4
2227
with:
23-
node-version: 18
24-
registry-url: "https://registry.npmjs.org/"
25-
always-auth: "true"
28+
node-version: 22
2629
cache: 'npm'
2730

31+
- name: Upgrade npm for OIDC support
32+
run: npm install -g npm@latest
33+
2834
- name: Configure GHR auth
2935
run: echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> ~/.npmrc
3036

@@ -39,14 +45,17 @@ jobs:
3945
tarball=$(npm pack --ignore-scripts | tail -n1)
4046
echo "tarball=$tarball" >> "$GITHUB_OUTPUT"
4147
48+
# npm publish uses OIDC trusted publishing (no token needed).
49+
# publish.sh adds --provenance for supply-chain attestations.
4250
- name: Publish to NPM
4351
env:
44-
NODE_AUTH_TOKEN: ${{ secrets.PUBLISH_REACT_TO_NPM_FROM_GITHUB }}
4552
TARBALL: ${{ steps.pack.outputs.tarball }}
53+
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }}
4654
run: scripts/publish.sh "https://registry.npmjs.org" "$TARBALL"
4755

4856
- name: Publish to GitHub Package Registry
4957
env:
5058
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5159
TARBALL: ${{ steps.pack.outputs.tarball }}
60+
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }}
5261
run: scripts/publish.sh "https://npm.pkg.github.com" "$TARBALL"

‎scripts/publish.sh‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,12 @@ trap 'rm -f "$body_file"' EXIT
8888
# curl -sS (no --fail) returns 0 for any HTTP response, non-zero only on
8989
# network/protocol errors — so we can cleanly separate "server answered" from
9090
# "could not reach server".
91+
auth_header=""
92+
if [[ -n "${NODE_AUTH_TOKEN:-}" ]]; then
93+
auth_header="Authorization: Bearer ${NODE_AUTH_TOKEN}"
94+
fi
9195
if ! http_code=$(curl -sS -m 30 -o "$body_file" -w '%{http_code}' \
92-
-H "Authorization: Bearer ${NODE_AUTH_TOKEN:-}" "$packument_url"); then
96+
${auth_header:+-H "$auth_header"} "$packument_url"); then
9397
echo "ERROR: request to ${packument_url} failed (network/timeout); refusing to publish on an ambiguous result." >&2
9498
exit 1
9599
fi
@@ -135,11 +139,16 @@ if [[ "$dry_run" == "true" ]]; then
135139
exit 0
136140
fi
137141

142+
provenance_flag=""
143+
if [[ "$registry" == *"registry.npmjs.org"* ]]; then
144+
provenance_flag="--provenance"
145+
fi
146+
138147
echo "Publishing ${pkg}@${version} (tag: ${tag}) to ${registry}"
139148
if [[ -n "$tarball" ]]; then
140149
# The tarball is a prebuilt artifact; skip lifecycle scripts (prepublishOnly
141150
# = test + build) that would otherwise run from the current package.json.
142-
npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts
151+
npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts $provenance_flag
143152
else
144-
npm publish --registry "$registry" --tag "$tag"
153+
npm publish --registry "$registry" --tag "$tag" $provenance_flag
145154
fi

0 commit comments

Comments
 (0)