From c804b8840b4907948ef926decee8212f9ddfacb2 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 10 Jun 2026 07:30:32 -0400 Subject: [PATCH 1/2] test(@angular/cli): remove unscoped authentication test cases from registry tests Modern package managers do not support unscoped authentication, and Yarn Classic's metadata command (yarn info) does not propagate unscoped credentials correctly, leading to 403 Forbidden failures on secure registries during ng update and ng add. This commit removes the unscoped authentication test cases from both add and update secure registry E2E tests, and cleans up the createNpmConfigForAuthentication helper to default to scoped authentication. --- .../e2e/tests/commands/add/secure-registry.ts | 26 +++---------------- .../tests/update/update-secure-registry.ts | 18 +------------ tests/e2e/utils/registry.ts | 2 +- 3 files changed, 5 insertions(+), 41 deletions(-) diff --git a/tests/e2e/tests/commands/add/secure-registry.ts b/tests/e2e/tests/commands/add/secure-registry.ts index 4a640607f8be..e585444b02e4 100644 --- a/tests/e2e/tests/commands/add/secure-registry.ts +++ b/tests/e2e/tests/commands/add/secure-registry.ts @@ -1,5 +1,5 @@ -import { expectFileNotToExist, expectFileToExist, rimraf } from '../../../utils/fs'; -import { getActivePackageManager, installWorkspacePackages } from '../../../utils/packages'; +import { expectFileNotToExist, expectFileToExist } from '../../../utils/fs'; +import { installWorkspacePackages } from '../../../utils/packages'; import { git, ng } from '../../../utils/process'; import { createNpmConfigForAuthentication } from '../../../utils/registry'; import { expectToFail } from '../../../utils/utils'; @@ -9,37 +9,17 @@ export default async function () { try { // The environment variable has priority over the .npmrc delete process.env['NPM_CONFIG_REGISTRY']; - const packageManager = getActivePackageManager(); - const supportsUnscopedAuth = packageManager === 'yarn'; const command = ['add', '@angular/pwa', '--skip-confirmation']; - // Works with unscoped registry authentication details - if (supportsUnscopedAuth) { - // Some package managers such as Bun and NPM do not support unscoped auth. - await createNpmConfigForAuthentication(false); - - await expectFileNotToExist('public/manifest.webmanifest'); - - await ng(...command); - await expectFileToExist('public/manifest.webmanifest'); - await git('clean', '-dxf'); - } - // Works with scoped registry authentication details await expectFileNotToExist('public/manifest.webmanifest'); - await createNpmConfigForAuthentication(true); + await createNpmConfigForAuthentication(); await ng(...command); await expectFileToExist('public/manifest.webmanifest'); await git('clean', '-dxf'); // Invalid authentication token - if (supportsUnscopedAuth) { - // Some package managers such as Bun and NPM do not support unscoped auth. - await createNpmConfigForAuthentication(false, true); - await expectToFail(() => ng(...command)); - } - await createNpmConfigForAuthentication(true, true); await expectToFail(() => ng(...command)); } finally { diff --git a/tests/e2e/tests/update/update-secure-registry.ts b/tests/e2e/tests/update/update-secure-registry.ts index b52d311a622f..3c0a9d468e44 100644 --- a/tests/e2e/tests/update/update-secure-registry.ts +++ b/tests/e2e/tests/update/update-secure-registry.ts @@ -6,9 +6,6 @@ import { getActivePackageManager } from '../../utils/packages'; import assert from 'node:assert'; export default async function () { - const packageManager = getActivePackageManager(); - const supportsUnscopedAuth = packageManager === 'yarn'; - // The environment variable has priority over the .npmrc delete process.env['NPM_CONFIG_REGISTRY']; const worksMessage = 'We analyzed your package.json'; @@ -20,15 +17,7 @@ export default async function () { // Valid authentication token - if (supportsUnscopedAuth) { - await createNpmConfigForAuthentication(false); - const { stdout: stdout1 } = await ng('update', ...extraArgs); - if (!stdout1.includes(worksMessage)) { - throw new Error(`Expected stdout to contain "${worksMessage}"`); - } - } - - await createNpmConfigForAuthentication(true); + await createNpmConfigForAuthentication(); const { stdout: stdout2 } = await ng('update', ...extraArgs); if (!stdout2.includes(worksMessage)) { throw new Error(`Expected stdout to contain "${worksMessage}"`); @@ -36,11 +25,6 @@ export default async function () { // Invalid authentication token - if (supportsUnscopedAuth) { - await createNpmConfigForAuthentication(false, true); - await expectToFail(() => ng('update', ...extraArgs)); - } - await createNpmConfigForAuthentication(true, true); await expectToFail(() => ng('update', ...extraArgs)); diff --git a/tests/e2e/utils/registry.ts b/tests/e2e/utils/registry.ts index fd557c116120..d8d75b566fb9 100644 --- a/tests/e2e/utils/registry.ts +++ b/tests/e2e/utils/registry.ts @@ -63,7 +63,7 @@ export async function createNpmConfigForAuthentication( * _auth="dGVzdGluZzpzM2NyZXQ="` * ``` */ - scopedAuthentication: boolean, + scopedAuthentication = true, /** When true, an incorrect token is used. Use this to validate authentication failures. */ invalidToken = false, ): Promise { From 5a2646b4761f1bb5a69ba16cb3c8422d803ffbce Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Tue, 9 Jun 2026 15:13:56 -0400 Subject: [PATCH 2/2] fix(@angular/cli): remove forceAuth and unscoped credential parsing Remove the non-standard forceAuth option and custom parsing for unscoped registry credentials (token, username, password, auth) in package-metadata.ts. Since the npm CLI does not support unscoped credentials and ignores them by default, this aligning removes the unnecessary parsing complexity. Unscoped credentials will now behave identically to any other standard configuration property, falling through to default configuration parsing. --- .../cli/src/utilities/package-metadata.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/angular/cli/src/utilities/package-metadata.ts b/packages/angular/cli/src/utilities/package-metadata.ts index fd31000f989a..05a739e898ae 100644 --- a/packages/angular/cli/src/utilities/package-metadata.ts +++ b/packages/angular/cli/src/utilities/package-metadata.ts @@ -52,9 +52,7 @@ export interface PackageManifest extends Manifest, NgPackageManifestProperties { peerDependenciesMeta?: Record; } -interface PackageManagerOptions extends Record { - forceAuth?: Record; -} +type PackageManagerOptions = Record; let npmrc: PackageManagerOptions; const npmPackageJsonCache = new Map>>(); @@ -175,19 +173,6 @@ function normalizeOptions( } switch (key) { - // Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them, - // even though they are documented. - // https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md - // https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91 - case '_authToken': - case 'token': - case 'username': - case 'password': - case '_auth': - case 'auth': - options['forceAuth'] ??= {}; - options['forceAuth'][key] = substitutedValue; - break; case 'noproxy': case 'no-proxy': options['noProxy'] = substitutedValue;