Skip to content

Commit 4fd2b24

Browse files
Merge pull request #8582 from Shopify/gonzalo/snapit-oidc-diagnostics
Diagnose npm OIDC failures without publishing
2 parents 7f8d4dd + a22660f commit 4fd2b24

1 file changed

Lines changed: 40 additions & 1 deletion

File tree

‎.github/workflows/release.yml‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ on:
2626
- nightly
2727
- latest
2828
- experimental
29+
diagnostics_only:
30+
description: 'Inspect npm OIDC authentication without publishing'
31+
default: false
32+
type: boolean
2933

3034
concurrency:
3135
group: changeset-${{ github.head_ref || github.run_id }}
@@ -39,7 +43,7 @@ jobs:
3943
# Snapit job - runs when /snapit comment is made on a PR
4044
snapit:
4145
name: Snapit
42-
if: ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/snapit' }}
46+
if: ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request && (github.event.comment.body == '/snapit' || github.event.comment.body == '/snapit-oidc') }}
4347
runs-on: ubuntu-latest
4448
permissions:
4549
contents: read
@@ -56,9 +60,42 @@ jobs:
5660
uses: ./.github/actions/setup-cli-deps
5761
with:
5862
node-version: 24.12.0
63+
- &npm-oidc-diagnostics
64+
name: Inspect npm OIDC authentication
65+
continue-on-error: true
66+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
67+
with:
68+
script: |
69+
const idToken = await core.getIDToken('npm:registry.npmjs.org');
70+
const [encodedHeader, encodedClaims] = idToken.split('.');
71+
const header = JSON.parse(Buffer.from(encodedHeader, 'base64url').toString());
72+
const claims = JSON.parse(Buffer.from(encodedClaims, 'base64url').toString());
73+
// Log only identity metadata, never the signed token or exchanged credential.
74+
const claimNames = [
75+
'iss', 'aud', 'sub', 'repository', 'workflow_ref', 'job_workflow_ref',
76+
'environment', 'runner_environment', 'event_name',
77+
];
78+
const identity = Object.fromEntries(claimNames.map((name) => [name, claims[name] ?? null]));
79+
core.info(JSON.stringify({alg: header.alg, kid: header.kid, claims: identity}, null, 2));
80+
81+
// Test the registry directly to separate identity rejection from publishing tooling.
82+
const response = await fetch('https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/@shopify%2fcli', {
83+
method: 'POST',
84+
headers: {Authorization: `Bearer ${idToken}`},
85+
signal: AbortSignal.timeout(10000),
86+
});
87+
core.info(`npm OIDC token exchange: HTTP ${response.status}`);
88+
for (const name of ['date', 'cf-ray', 'x-npm-request-id', 'x-github-request-id']) {
89+
const value = response.headers.get(name);
90+
if (value) core.info(`${name}: ${value}`);
91+
}
92+
// The response can contain an npm credential; never read or log its body.
93+
await response.body?.cancel();
5994
- name: Force snapshot changeset
95+
if: ${{ github.event.comment.body == '/snapit' }}
6096
run: "mv .changeset/force-snapshot-build.md.ignore .changeset/force-snapshot-build.md"
6197
- name: Create snapshot version
98+
if: ${{ github.event.comment.body == '/snapit' }}
6299
uses: Shopify/snapit@2a7ca29133cfeb2c8703654d54bc6dbc565caa69 # registry-and-package-manager
63100
with:
64101
comment_is_global: 'true'
@@ -202,7 +239,9 @@ jobs:
202239
uses: ./.github/actions/setup-cli-deps
203240
with:
204241
node-version: 24.12.0
242+
- *npm-oidc-diagnostics
205243
- name: Release
244+
if: ${{ !inputs.diagnostics_only }}
206245
run: pnpm release ${{ github.event.inputs.tag || 'nightly' }}
207246
env:
208247
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)