Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/cli/publish_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,8 @@ pub const PublishCommand = struct {
) PublishError!void {
const registry = ctx.manager.scopeForPackageName(ctx.package_name);

if (registry.token.len == 0 and (registry.url.password.len == 0 or registry.url.username.len == 0)) {
// Check for authentication: token, OR auth/user (username:password), OR URL-embedded credentials
if (registry.token.len == 0 and registry.auth.len == 0 and registry.user.len == 0 and (registry.url.password.len == 0 or registry.url.username.len == 0)) {
return error.NeedAuth;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

Expand Down
89 changes: 89 additions & 0 deletions test/cli/install/bun-publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -967,3 +967,92 @@ describe("--tolerate-republish", async () => {
expect(err).not.toContain("error:");
});
});

describe("split npmrc authentication", () => {
const authFormats = [
{
name: "split username and password",
scope: null,
packageName: "split-username-password-test",
async createNpmrc(registry: VerdaccioRegistry) {
const username = "testuser";
const password = "testpass123";
await registry.generateUser(username, password);

return `registry=http://localhost:${registry.port}/
//localhost:${registry.port}/:username=${username}
//localhost:${registry.port}/:_password=${Buffer.from(password).toString("base64")}
//localhost:${registry.port}/:email=${username}@example.com
`;
},
},
{
name: "split username and password with scoped registry",
scope: "@scoped",
packageName: "@scoped/split-scoped-test",
async createNpmrc(registry: VerdaccioRegistry) {
const username = "scopeduser";
const password = "scopedpass456";
await registry.generateUser(username, password);

return `@scoped:registry=http://localhost:${registry.port}/
//localhost:${registry.port}/:username=${username}
//localhost:${registry.port}/:_password=${Buffer.from(password).toString("base64")}
//localhost:${registry.port}/:email=${username}@example.com
`;
},
},
{
name: "split _authToken",
scope: null,
packageName: "split-authtoken-test",
async createNpmrc(registry: VerdaccioRegistry) {
const token = await registry.generateUser("tokenuser", "tokenpass");

return `registry=http://localhost:${registry.port}/
//localhost:${registry.port}/:_authToken=${token}
`;
},
},
{
name: "split _authToken with scoped registry",
scope: "@scoped",
packageName: "@scoped/split-token-test",
async createNpmrc(registry: VerdaccioRegistry) {
const token = await registry.generateUser("scopedtokenuser", "scopedtokenpass");

return `@scoped:registry=http://localhost:${registry.port}/
//localhost:${registry.port}/:_authToken=${token}
`;
},
},
];

for (const format of authFormats) {
test(`should authenticate with ${format.name}`, async () => {
const { packageDir, packageJson } = await registry.createTestDir();
const npmrcContent = await format.createNpmrc(registry);

const pkgJson = {
name: format.packageName,
version: "1.0.0",
};

const cleanupPath = format.scope
? join(registry.packagesPath, format.scope, format.packageName.split("/")[1])
: join(registry.packagesPath, format.packageName);

await Promise.all([
rm(cleanupPath, { recursive: true, force: true }),
write(join(packageDir, ".npmrc"), npmrcContent),
write(packageJson, JSON.stringify(pkgJson)),
]);

const { out, err, exitCode } = await publish(env, packageDir);

expect(out).toContain(`+ ${format.packageName}@1.0.0`);
expect(err).not.toContain("missing authentication");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
expect(exitCode).toBe(0);
});
}
});