Skip to content

Migrate npm OIDC#230

Merged
subtleGradient merged 4 commits intomainfrom
migrate-npm-oidc
Nov 12, 2025
Merged

Migrate npm OIDC#230
subtleGradient merged 4 commits intomainfrom
migrate-npm-oidc

Conversation

@subtleGradient
Copy link
Contributor

No description provided.

Replace token-based authentication with OpenID Connect (OIDC) for enhanced security:
- Remove NPM_TOKEN from changesets action environment
- Enable automatic provenance generation (NPM_CONFIG_PROVENANCE)
- Set NODE_AUTH_TOKEN to empty string to force OIDC authentication
- Keep diagnostic step for troubleshooting auth issues

This requires configuring npm Trusted Publisher on npmjs.com for the repository.
Split changesets/action into versioning-only step and separate publish step
to avoid .npmrc token mutation that would block OIDC. This ensures npm uses
Trusted Publisher authentication instead of legacy token-based auth.

Key changes:
- changesets/action now only runs version step (no publish input)
- Added OIDC preflight step to scrub any auth tokens from .npmrc before publishing
- Separated publish into dedicated step with pnpm changeset-publish
- Added post-mortem diagnostics for troubleshooting publish failures

The id-token: write permission remains enabled for OIDC token generation.
Registry setup and package.json publish script already include provenance.
Address three critical risks identified during code review:

1. Gate publish step to run only when no changesets exist
   - Prevents wasteful runs and log noise when version PR is created
   - Ensures publish only runs on merge commits (when hasChangesets == 'false')
   - .github/workflows/publish.yaml:74

2. Harden preflight to scrub all .npmrc locations and auth forms
   - Iterate over all potential locations: NPM_CONFIG_USERCONFIG, ~/.npmrc, .npmrc
   - Remove both registry-scoped (_authToken, _auth=) and global (always-auth) forms
   - Prevents stray .npmrc from blocking OIDC authentication
   - .github/workflows/publish.yaml:45-52

3. Make whoami check robust with exit code instead of grep
   - Check npm whoami exit code (0 = logged in, non-zero = not logged in)
   - Avoids reliance on fragile message matching across npm versions
   - Verify registry config (including @openrouter scope)
   - .github/workflows/publish.yaml:62-71

4. Enhance post-mortem diagnostics
   - Log registry configuration for both global and @openrouter scope
   - Redact .npmrc contents to aid forensics without exposing secrets
   - Check all three potential .npmrc locations
   - .github/workflows/publish.yaml:92-101

With these changes, the workflow is hardened against OIDC authentication failures.
…rubbing

Improve sed patterns to handle edge cases in token removal:
- Registry-scoped tokens: Allow optional whitespace around = sign
  sed -i -E '/\/\/registry\.npmjs\.org\/:(_authToken|_auth)\s*=/d'
- Global tokens: Match _authToken or _auth anywhere on a line with leading whitespace
  sed -i -E '/^\s*(_authToken|_auth)\s*=/d'
- Global always-auth: Case-insensitive with optional spacing
  sed -i -E '/^\s*[Aa]lways-[Aa]uth\s*=/d'

This closes the gap where npm could treat whitespace-padded or variant token
lines as a signal to use legacy token auth instead of OIDC. Extended regex (-E)
enables more flexible matching without sacrificing readability.

Addresses edge case identified during workflow review.
Copilot AI review requested due to automatic review settings November 12, 2025 20:38
@subtleGradient subtleGradient merged commit 563f4ab into main Nov 12, 2025
7 checks passed
@subtleGradient subtleGradient deleted the migrate-npm-oidc branch November 12, 2025 20:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR migrates the npm publishing workflow from using a manual NPM_TOKEN secret to OIDC (OpenID Connect) authentication. This enhances security by eliminating the need to store long-lived npm tokens as GitHub secrets, instead leveraging GitHub's built-in OIDC token provider for authenticated npm publishing.

Key Changes:

  • Separated changesets version and publish steps for better control over the release process
  • Implemented OIDC preflight step that scrubs legacy auth tokens from .npmrc files to ensure OIDC is used
  • Configured publishing to use npm provenance with --provenance flag for supply chain security
  • Updated diagnostic steps to focus on OIDC-specific validation and post-mortem analysis

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if [ -n "$npmrc" ] && [ -f "$npmrc" ]; then
echo " $npmrc:"
echo " Lines: $(wc -l < "$npmrc")"
echo " Auth lines: $(grep -c "_auth\|_token" "$npmrc" || echo "0")"
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The grep -c command will exit with status 1 when no matches are found, but the pattern "_auth\|_token" uses BRE (Basic Regular Expression) syntax. When using grep without -E, the pipe character | needs to be escaped as \| which is correct here. However, for better portability and clarity, consider using grep -E (or egrep) with unescaped |:

echo "    Auth lines: $(grep -cE "_auth|_token" "$npmrc" || echo "0")"

Alternatively, to match the pattern more precisely (matching the actual config key names), consider:

echo "    Auth lines: $(grep -cE "(_authToken|_auth|_token)\s*=" "$npmrc" || echo "0")"
Suggested change
echo " Auth lines: $(grep -c "_auth\|_token" "$npmrc" || echo "0")"
echo " Auth lines: $(grep -cE '(_authToken|_auth|_token)\s*=' "$npmrc" || echo "0")"

Copilot uses AI. Check for mistakes.
echo " Lines: $(wc -l < "$npmrc")"
echo " Auth lines: $(grep -c "_auth\|_token" "$npmrc" || echo "0")"
echo " Content (redacted):"
sed 's/\(_auth[^=]*=\).*/\1***REDACTED***/g; s/\(_token[^=]*=\).*/\1***REDACTED***/g' "$npmrc" | sed 's/^/ /'
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The sed pattern matching on line 100 is inconsistent with the cleanup patterns used earlier (lines 49-53). The cleanup patterns match _authToken and _auth as distinct keys, but this redaction pattern uses a more generic approach with _auth[^=]* which would match both.

For consistency and accuracy, consider aligning the redaction pattern with the cleanup patterns:

sed 's/\(_authToken\s*=\).*/\1***REDACTED***/g; s/\(_auth\s*=\).*/\1***REDACTED***/g' "$npmrc" | sed 's/^/      /'

This ensures you're redacting the same patterns you're cleaning up.

Suggested change
sed 's/\(_auth[^=]*=\).*/\1***REDACTED***/g; s/\(_token[^=]*=\).*/\1***REDACTED***/g' "$npmrc" | sed 's/^/ /'
sed 's/\(_authToken\s*=\).*/\1***REDACTED***/g; s/\(_auth\s*=\).*/\1***REDACTED***/g' "$npmrc" | sed 's/^/ /'

Copilot uses AI. Check for mistakes.
castortech pushed a commit to castortech/rivet-ai-sdk-provider that referenced this pull request Dec 28, 2025
Their comments:
Latest commit:

	Upgrade npm to support OIDC Trusted Publishing (OpenRouterTeam#231)
	npm v10.8.2 (bundled with Node 20.x) predates Trusted Publishing support.
	npm CLI v11.5.1+ is required for OIDC authentication with --provenance flag.

	Evidence: CI publish failed with ENEEDAUTH despite id-token: write and
	correct workflow setup. npm fell back to token auth and found none.

	Add explicit npm upgrade step before publish to ensure OIDC works.

Previous:
Migrate npm OIDC (OpenRouterTeam#230)
* refactor: migrate to OIDC for npm package publishing

Replace token-based authentication with OpenID Connect (OIDC) for enhanced security:
- Remove NPM_TOKEN from changesets action environment
- Enable automatic provenance generation (NPM_CONFIG_PROVENANCE)
- Set NODE_AUTH_TOKEN to empty string to force OIDC authentication
- Keep diagnostic step for troubleshooting auth issues

This requires configuring npm Trusted Publisher on npmjs.com for the repository.

* refactor: restructure publish workflow to support OIDC properly

Split changesets/action into versioning-only step and separate publish step
to avoid .npmrc token mutation that would block OIDC. This ensures npm uses
Trusted Publisher authentication instead of legacy token-based auth.

Key changes:
- changesets/action now only runs version step (no publish input)
- Added OIDC preflight step to scrub any auth tokens from .npmrc before publishing
- Separated publish into dedicated step with pnpm changeset-publish
- Added post-mortem diagnostics for troubleshooting publish failures

The id-token: write permission remains enabled for OIDC token generation.
Registry setup and package.json publish script already include provenance.

* fix: harden OIDC workflow with publish gate and comprehensive preflight

Address three critical risks identified during code review:

1. Gate publish step to run only when no changesets exist
   - Prevents wasteful runs and log noise when version PR is created
   - Ensures publish only runs on merge commits (when hasChangesets == 'false')
   - .github/workflows/publish.yaml:74

2. Harden preflight to scrub all .npmrc locations and auth forms
   - Iterate over all potential locations: NPM_CONFIG_USERCONFIG, ~/.npmrc, .npmrc
   - Remove both registry-scoped (_authToken, _auth=) and global (always-auth) forms
   - Prevents stray .npmrc from blocking OIDC authentication
   - .github/workflows/publish.yaml:45-52

3. Make whoami check robust with exit code instead of grep
   - Check npm whoami exit code (0 = logged in, non-zero = not logged in)
   - Avoids reliance on fragile message matching across npm versions
   - Verify registry config (including @openrouter scope)
   - .github/workflows/publish.yaml:62-71

4. Enhance post-mortem diagnostics
   - Log registry configuration for both global and @openrouter scope
   - Redact .npmrc contents to aid forensics without exposing secrets
   - Check all three potential .npmrc locations
   - .github/workflows/publish.yaml:92-101

With these changes, the workflow is hardened against OIDC authentication failures.

* refine: use extended regex patterns for comprehensive .npmrc token scrubbing

Improve sed patterns to handle edge cases in token removal:
- Registry-scoped tokens: Allow optional whitespace around = sign
  sed -i -E '/\/\/registry\.npmjs\.org\/:(_authToken|_auth)\s*=/d'
- Global tokens: Match _authToken or _auth anywhere on a line with leading whitespace
  sed -i -E '/^\s*(_authToken|_auth)\s*=/d'
- Global always-auth: Case-insensitive with optional spacing
  sed -i -E '/^\s*[Aa]lways-[Aa]uth\s*=/d'

This closes the gap where npm could treat whitespace-padded or variant token
lines as a signal to use legacy token auth instead of OIDC. Extended regex (-E)
enables more flexible matching without sacrificing readability.

Addresses edge case identified during workflow review.
kesavan-byte pushed a commit to osm-API/ai-sdk-provider that referenced this pull request Feb 13, 2026
* refactor: migrate to OIDC for npm package publishing

Replace token-based authentication with OpenID Connect (OIDC) for enhanced security:
- Remove NPM_TOKEN from changesets action environment
- Enable automatic provenance generation (NPM_CONFIG_PROVENANCE)
- Set NODE_AUTH_TOKEN to empty string to force OIDC authentication
- Keep diagnostic step for troubleshooting auth issues

This requires configuring npm Trusted Publisher on npmjs.com for the repository.

* refactor: restructure publish workflow to support OIDC properly

Split changesets/action into versioning-only step and separate publish step
to avoid .npmrc token mutation that would block OIDC. This ensures npm uses
Trusted Publisher authentication instead of legacy token-based auth.

Key changes:
- changesets/action now only runs version step (no publish input)
- Added OIDC preflight step to scrub any auth tokens from .npmrc before publishing
- Separated publish into dedicated step with pnpm changeset-publish
- Added post-mortem diagnostics for troubleshooting publish failures

The id-token: write permission remains enabled for OIDC token generation.
Registry setup and package.json publish script already include provenance.

* fix: harden OIDC workflow with publish gate and comprehensive preflight

Address three critical risks identified during code review:

1. Gate publish step to run only when no changesets exist
   - Prevents wasteful runs and log noise when version PR is created
   - Ensures publish only runs on merge commits (when hasChangesets == 'false')
   - .github/workflows/publish.yaml:74

2. Harden preflight to scrub all .npmrc locations and auth forms
   - Iterate over all potential locations: NPM_CONFIG_USERCONFIG, ~/.npmrc, .npmrc
   - Remove both registry-scoped (_authToken, _auth=) and global (always-auth) forms
   - Prevents stray .npmrc from blocking OIDC authentication
   - .github/workflows/publish.yaml:45-52

3. Make whoami check robust with exit code instead of grep
   - Check npm whoami exit code (0 = logged in, non-zero = not logged in)
   - Avoids reliance on fragile message matching across npm versions
   - Verify registry config (including @openrouter scope)
   - .github/workflows/publish.yaml:62-71

4. Enhance post-mortem diagnostics
   - Log registry configuration for both global and @openrouter scope
   - Redact .npmrc contents to aid forensics without exposing secrets
   - Check all three potential .npmrc locations
   - .github/workflows/publish.yaml:92-101

With these changes, the workflow is hardened against OIDC authentication failures.

* refine: use extended regex patterns for comprehensive .npmrc token scrubbing

Improve sed patterns to handle edge cases in token removal:
- Registry-scoped tokens: Allow optional whitespace around = sign
  sed -i -E '/\/\/registry\.npmjs\.org\/:(_authToken|_auth)\s*=/d'
- Global tokens: Match _authToken or _auth anywhere on a line with leading whitespace
  sed -i -E '/^\s*(_authToken|_auth)\s*=/d'
- Global always-auth: Case-insensitive with optional spacing
  sed -i -E '/^\s*[Aa]lways-[Aa]uth\s*=/d'

This closes the gap where npm could treat whitespace-padded or variant token
lines as a signal to use legacy token auth instead of OIDC. Extended regex (-E)
enables more flexible matching without sacrificing readability.

Addresses edge case identified during workflow review.
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