@@ -58,14 +58,31 @@ export async function publish(args) {
5858 logger . info ( `Package: ${ packageJson . name } @${ packageJson . version } ` ) ;
5959 logger . info ( `Build time: ${ buildTime } ` ) ;
6060 logger . info ( `Short SHA: ${ shortSha } ` ) ;
61- logger . info ( `Token: '${ obfuscateToken ( args . accessToken ) } '` ) ;
61+ if ( args . useOidc ) {
62+ logger . info ( `Authentication: OIDC (Trusted Publishing)` ) ;
63+ } else {
64+ logger . info ( `Token: '${ obfuscateToken ( args . accessToken ) } '` ) ;
65+ }
6266 logger . info ( `Repository: ${ repoUrl } ` ) ;
6367 logger . info ( `Last commit message: ${ commitMessage } ` ) ;
6468 logger . info ( `Last commit author: ${ commitAuthorWithEmail } ` ) ;
6569 logger . info ( `Registry: ${ args . registry || 'https://registry.npmjs.org/' } ` ) ;
6670
67- if ( ! args . accessToken ?. length ) {
68- logger . warn ( `No access token provided. Publishing to registry ${ args . registry } may fail.` ) ;
71+ if ( ! args . useOidc && ! args . accessToken ?. length ) {
72+ logger . warn ( `No access token provided and OIDC not enabled. Publishing to registry ${ args . registry } may fail.` ) ;
73+ }
74+ if ( args . useOidc ) {
75+ logger . info ( `OIDC authentication enabled. Ensure your CI/CD has id-token: write permissions and a trusted publisher is configured on npmjs.com.` ) ;
76+ // Check npm version for OIDC support
77+ const npmVersionResult = tryExecSync ( 'npm --version' , { cwd : packageDirectory } ) ;
78+ if ( npmVersionResult . success ) {
79+ const npmVersion = npmVersionResult . output . trim ( ) ;
80+ const [ major , minor ] = npmVersion . split ( '.' ) . map ( Number ) ;
81+ logger . info ( `npm version: ${ npmVersion } ` ) ;
82+ if ( major < 11 || ( major === 11 && minor < 5 ) ) {
83+ logger . warn ( `⚠ npm version ${ npmVersion } may not support OIDC. Version 11.5+ is recommended.` ) ;
84+ }
85+ }
6986 }
7087
7188 // Remove slahes from the end of the tag (this may happen if the tag is provided by github ref_name
@@ -126,7 +143,7 @@ export async function publish(args) {
126143 msg += `Commit URL: ${ repoUrl } /commit/${ shortSha } \n` ;
127144 msg += `Build time: ${ buildTime } \n` ;
128145 msg += `Registry: ${ args . registry } \n` ;
129- msg += `Token : ${ obfuscateToken ( args . accessToken ) } \n` ;
146+ msg += `Auth : ${ args . useOidc ? 'OIDC (Trusted Publishing)' : obfuscateToken ( args . accessToken ) } \n` ;
130147 msg += `Tag: ${ args . tag || '-' } ${ args . useTagInVersion ? ' (version+tag)' : '' } ${ args . createGitTag ? ' (creating git tag)' : '' } \n` ;
131148 msg += "```" ;
132149 await sendMessageToWebhook ( webhook , msg , { logger } ) ;
@@ -197,13 +214,13 @@ export async function publish(args) {
197214 // Default env
198215 const env = {
199216 ...process . env ,
200- NPM_TOKEN : args . accessToken || undefined ,
217+ NPM_TOKEN : args . useOidc ? undefined : ( args . accessToken || undefined ) ,
201218 NPM_CONFIG_REGISTRY : ( args . registry || 'https://registry.npmjs.org/' ) ,
202219 }
203220
204221
205222 // set config
206- {
223+ if ( ! args . useOidc ) {
207224 let registryUrlWithoutScheme = ( args . registry || 'https://registry.npmjs.org/' ) . replace ( / h t t p s ? : \/ \/ / , '' ) ;
208225 if ( ! registryUrlWithoutScheme . endsWith ( '/' ) ) registryUrlWithoutScheme += '/' ;
209226
@@ -223,6 +240,34 @@ export async function publish(args) {
223240 // env
224241 // });
225242 // }
243+ } else {
244+ logger . info ( `Skipping npm config auth token setup - using OIDC authentication` ) ;
245+
246+ // Check for OIDC environment variables (GitHub Actions sets these)
247+ const hasOidcEnv = ! ! ( process . env . ACTIONS_ID_TOKEN_REQUEST_URL && process . env . ACTIONS_ID_TOKEN_REQUEST_TOKEN ) ;
248+ if ( hasOidcEnv ) {
249+ logger . info ( `✓ GitHub Actions OIDC environment detected` ) ;
250+ logger . info ( ` ACTIONS_ID_TOKEN_REQUEST_URL: ${ process . env . ACTIONS_ID_TOKEN_REQUEST_URL ? '(set)' : '(not set)' } ` ) ;
251+ logger . info ( ` ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${ process . env . ACTIONS_ID_TOKEN_REQUEST_TOKEN ? '(set)' : '(not set)' } ` ) ;
252+ } else {
253+ logger . warn ( `⚠ GitHub Actions OIDC environment variables not detected!` ) ;
254+ logger . warn ( ` This may indicate:` ) ;
255+ logger . warn ( ` - The workflow doesn't have 'id-token: write' permission` ) ;
256+ logger . warn ( ` - Not running in GitHub Actions` ) ;
257+ logger . warn ( ` - Running on a self-hosted runner without OIDC support` ) ;
258+ }
259+
260+ // Clear any existing auth tokens that might interfere with OIDC
261+ // npm OIDC works best when there's no conflicting token configuration
262+ let registryUrlWithoutScheme = ( args . registry || 'https://registry.npmjs.org/' ) . replace ( / h t t p s ? : \/ \/ / , '' ) ;
263+ if ( ! registryUrlWithoutScheme . endsWith ( '/' ) ) registryUrlWithoutScheme += '/' ;
264+ try {
265+ const clearCmd = `npm config delete //${ registryUrlWithoutScheme } :_authToken` ;
266+ logger . info ( `Clearing any existing auth token for OIDC: ${ clearCmd } ` ) ;
267+ tryExecSync ( clearCmd , { cwd : packageDirectory , env } ) ;
268+ } catch {
269+ // Ignore errors - token might not exist
270+ }
226271 }
227272
228273 const htmlUrl = args . registry ?. includes ( "npmjs" ) ? `https://www.npmjs.com/package/${ packageJson . name } /v/${ packageJson . version } ` : ( args . registry + `/${ packageJson . name } ` ) ;
@@ -257,7 +302,12 @@ export async function publish(args) {
257302 logger . info ( `Package view result ${ packageVersionPublished } ` ) ;
258303
259304
260- let cmd = `npm publish --access public`
305+ const registryUrl = args . registry || 'https://registry.npmjs.org/' ;
306+ let cmd = `npm publish --access public --registry ${ registryUrl } `
307+ if ( args . useOidc ) {
308+ cmd += ' --provenance' ;
309+ logger . info ( `OIDC authentication enabled, adding --provenance flag for trusted publishing.` ) ;
310+ }
261311 if ( dryRun ) {
262312 cmd += ' --dry-run' ;
263313 logger . info ( `Dry run mode enabled, not actually publishing package.` ) ;
@@ -294,8 +344,84 @@ export async function publish(args) {
294344 }
295345 else {
296346 logger . error ( `❌ Failed to publish package ${ packageJson . name } @${ packageJson . version } \n${ res . error } ` ) ;
347+
348+ // Provide helpful OIDC troubleshooting information if OIDC was enabled
349+ if ( args . useOidc ) {
350+ const errorStr = res . error ?. toString ( ) || res . output ?. toString ( ) || '' ;
351+ const is404Error = errorStr . includes ( '404' ) || errorStr . includes ( 'Not found' ) || errorStr . includes ( 'Not Found' ) ;
352+ const is401Error = errorStr . includes ( '401' ) || errorStr . includes ( 'Unauthorized' ) ;
353+ const is403Error = errorStr . includes ( '403' ) || errorStr . includes ( 'Forbidden' ) ;
354+ const isNeedAuthError = errorStr . includes ( 'ENEEDAUTH' ) || errorStr . includes ( 'need auth' ) || errorStr . includes ( 'You need to authorize' ) ;
355+
356+ logger . error ( `\n${ '=' . repeat ( 80 ) } ` ) ;
357+ logger . error ( `OIDC TROUBLESHOOTING GUIDE` ) ;
358+ logger . error ( `${ '=' . repeat ( 80 ) } ` ) ;
359+
360+ if ( isNeedAuthError ) {
361+ logger . error ( `\n⚠️ ERROR: ENEEDAUTH - Authentication Required` ) ;
362+ logger . error ( ` npm is not detecting the OIDC environment. This usually means:\n` ) ;
363+ logger . error ( ` a) MISSING PERMISSIONS: Workflow needs 'id-token: write' permission` ) ;
364+ logger . error ( ` Add to your workflow:` ) ;
365+ logger . error ( ` permissions:` ) ;
366+ logger . error ( ` contents: read` ) ;
367+ logger . error ( ` id-token: write\n` ) ;
368+ logger . error ( ` b) NPM VERSION TOO OLD: OIDC requires npm >= 11.5` ) ;
369+ logger . error ( ` Update Node.js to v22+ or run: npm install -g npm@latest\n` ) ;
370+ logger . error ( ` c) NOT IN GITHUB ACTIONS: OIDC only works in supported CI environments` ) ;
371+ logger . error ( ` Currently supported: GitHub Actions, GitLab CI/CD\n` ) ;
372+ logger . error ( ` d) SELF-HOSTED RUNNER: OIDC requires cloud-hosted runners\n` ) ;
373+ logger . error ( ` Environment check:` ) ;
374+ logger . error ( ` ACTIONS_ID_TOKEN_REQUEST_URL: ${ process . env . ACTIONS_ID_TOKEN_REQUEST_URL ? '✓ set' : '✗ NOT SET' } ` ) ;
375+ logger . error ( ` ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${ process . env . ACTIONS_ID_TOKEN_REQUEST_TOKEN ? '✓ set' : '✗ NOT SET' } ` ) ;
376+ logger . error ( ` GITHUB_ACTIONS: ${ process . env . GITHUB_ACTIONS ? '✓ set' : '✗ NOT SET' } \n` ) ;
377+ } else if ( is404Error ) {
378+ logger . error ( `\n⚠️ ERROR: 404 Not Found` ) ;
379+ logger . error ( ` This usually means one of the following:\n` ) ;
380+ logger . error ( ` a) FIRST-TIME PUBLISH: The package doesn't exist on npmjs.com yet.` ) ;
381+ logger . error ( ` → First publish MUST be done with a token: --access-token <token>` ) ;
382+ logger . error ( ` → After first publish, configure trusted publisher, then use --oidc\n` ) ;
383+ logger . error ( ` b) TRUSTED PUBLISHER NOT CONFIGURED for this package.` ) ;
384+ logger . error ( ` → Go to: https://www.npmjs.com/package/${ packageJson . name } /access` ) ;
385+ logger . error ( ` → Click "Settings" → "Trusted Publisher" → "GitHub Actions"` ) ;
386+ logger . error ( ` → Configure: organization/user, repository, workflow filename\n` ) ;
387+ logger . error ( ` c) WORKFLOW FILENAME MISMATCH` ) ;
388+ logger . error ( ` → The workflow filename on npmjs.com must EXACTLY match your .yml file` ) ;
389+ logger . error ( ` → Check for typos, case sensitivity, and path differences\n` ) ;
390+ } else if ( is401Error ) {
391+ logger . error ( `\n⚠️ ERROR: 401 Unauthorized` ) ;
392+ logger . error ( ` The OIDC token was not accepted. Check:\n` ) ;
393+ logger . error ( ` a) GitHub Actions must have 'id-token: write' permission` ) ;
394+ logger . error ( ` b) Trusted publisher must be configured on npmjs.com` ) ;
395+ logger . error ( ` c) npm version must be >= 11.5\n` ) ;
396+ } else if ( is403Error ) {
397+ logger . error ( `\n⚠️ ERROR: 403 Forbidden` ) ;
398+ logger . error ( ` Access denied. Check:\n` ) ;
399+ logger . error ( ` a) You have publish permissions for this package` ) ;
400+ logger . error ( ` b) Trusted publisher configuration matches your workflow exactly` ) ;
401+ logger . error ( ` c) Repository owner/name matches the trusted publisher config\n` ) ;
402+ } else {
403+ logger . error ( `\nOIDC authentication failed. Please check the following:\n` ) ;
404+ }
405+
406+ logger . error ( `CHECKLIST:` ) ;
407+ logger . error ( ` □ Package exists on npmjs.com (first publish requires --access-token)` ) ;
408+ logger . error ( ` □ Trusted publisher configured: https://www.npmjs.com/package/${ packageJson . name } /access` ) ;
409+ logger . error ( ` □ Workflow has 'id-token: write' permission` ) ;
410+ logger . error ( ` □ npm version >= 11.5 (current: run 'npm --version' to check)` ) ;
411+ logger . error ( ` □ Using cloud-hosted runner (not self-hosted)` ) ;
412+ logger . error ( ` □ Workflow filename matches exactly (case-sensitive)\n` ) ;
413+ logger . error ( `DOCUMENTATION:` ) ;
414+ logger . error ( ` → npm Trusted Publishing: https://docs.npmjs.com/trusted-publishers/` ) ;
415+ logger . error ( ` → npm Provenance: https://docs.npmjs.com/generating-provenance-statements/` ) ;
416+ logger . error ( `${ '=' . repeat ( 80 ) } \n` ) ;
417+ }
418+
297419 if ( webhook ) {
298- await sendMessageToWebhookWithCodeblock ( webhook , `❌ **Failed to publish package** \`${ packageJson . name } @${ packageJson . version } \`:` , res . error , { logger } ) ;
420+ let errorMsg = `❌ **Failed to publish package** \`${ packageJson . name } @${ packageJson . version } \`:` ;
421+ if ( args . useOidc ) {
422+ errorMsg += `\n⚠️ OIDC was enabled. See logs for troubleshooting guide or visit: https://docs.npmjs.com/trusted-publishers/` ;
423+ }
424+ await sendMessageToWebhookWithCodeblock ( webhook , errorMsg , res . error , { logger } ) ;
299425 }
300426 throw new Error ( `Failed to publish package ${ packageJson . name } @${ packageJson . version } : ${ res . error } ` ) ;
301427 }
0 commit comments