Skip to content

Commit e6a8daf

Browse files
authored
Fix Trusted Publishing via OIDC (#777)
1 parent 45404ba commit e6a8daf

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
lines changed

source/cli-implementation.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import config from './config.js';
1010
import * as util from './util.js';
1111
import * as git from './git-util.js';
1212
import * as npm from './npm/util.js';
13+
import {getOidcProvider} from './npm/oidc.js';
1314
import {SEMVER_INCREMENTS} from './version.js';
1415
import ui from './ui.js';
1516
import np from './index.js';
@@ -193,18 +194,23 @@ try {
193194

194195
// Check authentication early, before Listr starts (so login can be interactive)
195196
if (options.runPublish) {
196-
const externalRegistry = npm.isExternalRegistry(package_)
197-
? package_.publishConfig.registry
198-
: false;
199-
200-
try {
201-
await npm.username({externalRegistry});
202-
} catch (error) {
203-
if (error.isNotLoggedIn && isInteractive()) {
204-
console.log('\nYou must be logged in to publish. Running `npm login`...\n');
205-
await npm.login({externalRegistry});
206-
} else {
207-
throw error;
197+
// Skip auth check if OIDC is available (will be handled by npm publish itself)
198+
if (getOidcProvider()) {
199+
console.log('OIDC authentication detected - skipping auth check');
200+
} else {
201+
const externalRegistry = npm.isExternalRegistry(package_)
202+
? package_.publishConfig.registry
203+
: false;
204+
205+
try {
206+
await npm.username({externalRegistry});
207+
} catch (error) {
208+
if (error.isNotLoggedIn && isInteractive()) {
209+
console.log('\nYou must be logged in to publish. Running `npm login`...\n');
210+
await npm.login({externalRegistry});
211+
} else {
212+
throw error;
213+
}
208214
}
209215
}
210216
}

source/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import gitTasks from './git-tasks.js';
2323
import {getPackagePublishArguments, runPublish} from './npm/publish.js';
2424
import enable2fa, {getEnable2faArguments} from './npm/enable-2fa.js';
2525
import handleNpmError from './npm/handle-npm-error.js';
26+
import {getOidcProvider} from './npm/oidc.js';
2627
import releaseTaskHelper from './release-task-helper.js';
2728
import {findLockfile, printCommand} from './package-manager/index.js';
2829
import * as util from './util.js';
@@ -110,7 +111,8 @@ const np = async (input = 'patch', {packageManager, ...options}, {package_, root
110111
}
111112
}, {wait: 2000});
112113

113-
const shouldEnable2FA = options['2fa'] && options.availability.isAvailable && !options.availability.isUnknown && !package_.private && !npm.isExternalRegistry(package_);
114+
// Don't enable 2FA when using OIDC (Trusted Publishing) as it's already managed
115+
const shouldEnable2FA = options['2fa'] && options.availability.isAvailable && !options.availability.isUnknown && !package_.private && !npm.isExternalRegistry(package_) && !getOidcProvider();
114116

115117
// To prevent the process from hanging due to watch mode (e.g. when running `vitest`)
116118
const ciEnvOptions = {env: {CI: 'true'}};

test/index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,42 @@ test('skip enabling 2FA if the `2fa` option is false', async t => {
129129
t.true(enable2faStub.notCalled);
130130
});
131131

132+
test('skip enabling 2FA in trusted publishing (OIDC) contexts', async t => {
133+
const enable2faStub = sinon.stub();
134+
135+
/** @type {typeof np} */
136+
const npMock = await esmock('../source/index.js', {
137+
del: {deleteAsync: sinon.stub()},
138+
execa: {execa: sinon.stub().returns(fakeExecaReturn())},
139+
'../source/prerequisite-tasks.js': sinon.stub(),
140+
'../source/git-tasks.js': sinon.stub(),
141+
'../source/git-util.js': {
142+
hasUpstream: sinon.stub().returns(true),
143+
pushGraceful: sinon.stub(),
144+
verifyWorkingTreeIsClean: sinon.stub(),
145+
},
146+
'../source/npm/enable-2fa.js': enable2faStub,
147+
'../source/npm/publish.js': {
148+
getPackagePublishArguments: sinon.stub().returns([]),
149+
runPublish: sinon.stub().returns(fakeObservableReturn()),
150+
},
151+
'../source/npm/oidc.js': {
152+
getOidcProvider: () => 'github',
153+
},
154+
});
155+
156+
await t.notThrowsAsync(npMock('1.0.0', {
157+
...defaultOptions,
158+
availability: {
159+
isAvailable: true,
160+
isUnknown: false,
161+
},
162+
'2fa': true,
163+
}, npPackageResult));
164+
165+
t.true(enable2faStub.notCalled);
166+
});
167+
132168
test('rollback is called when publish fails', async t => {
133169
const deleteTagStub = sinon.stub().resolves();
134170
const removeLastCommitStub = sinon.stub().resolves();

0 commit comments

Comments
 (0)