diff --git a/content/nav.yml b/content/nav.yml index f042ffffc86..a72f424dd63 100644 --- a/content/nav.yml +++ b/content/nav.yml @@ -158,6 +158,8 @@ url: /generating-provenance-statements - title: Trusted publishing with OIDC url: /trusted-publishers + - title: Staged publishing + url: /staged-publishing - title: About ECDSA registry signatures url: /about-registry-signatures - title: Verifying ECDSA registry signatures diff --git a/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-private-packages.mdx b/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-private-packages.mdx index 9c6de2e7447..8031e953743 100644 --- a/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-private-packages.mdx +++ b/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-private-packages.mdx @@ -81,16 +81,19 @@ npm install my-package By default, scoped packages are published with private visibility. - +There are two ways to publish your package to the npm registry: -**Important:** Publishing to npm requires either: +1. [Direct publishing](#direct-publishing) +2. [Staged publishing](#staged-publishing) -- Two-factor authentication (2FA) enabled on your account, OR -- A granular access token with bypass 2FA enabled +### Direct publishing -For more information, see the npm documentation on [requiring 2FA for package publishing](/requiring-2fa-for-package-publishing-and-settings-modification). +To publish directly with `npm publish`, you need either: - +- Two-factor authentication (2FA) enabled on your account, or +- A granular access token (GAT) with bypass 2FA enabled + +For more information, see the npm documentation on [requiring 2FA for package publishing](/requiring-2fa-for-package-publishing-and-settings-modification). 1. On the command line, navigate to the root directory of your package. @@ -110,6 +113,42 @@ For more information, see the npm documentation on [requiring 2FA for package pu For more information on the `publish` command, see the [CLI documentation][cli-publish]. +### Staged publishing + +Instead of publishing directly, you can stage your package and approve it later. Staging the package does not require 2FA, which allows CI workflows to submit a package to the staging area. Before the package is published to the registry, a maintainer must review and approve it with 2FA. + +A GAT with bypass 2FA does not bypass the 2FA check during staged package approval. + +1. On the command line, navigate to the root directory of your package. + + ``` + cd /path/to/package + ``` + +2. To stage your package, run: + + ``` + npm stage publish + ``` + + This submits your package to a staging area. + +3. To check that your package has been staged, use either of the following methods: + - In the CLI, run `npm stage list ` to find the staged package and its stage ID. + - On [npmjs.com](https://www.npmjs.com), open the **Staged Packages** tab to review staged packages. + +4. To approve and publish the staged package, use one of the following methods: + - In the CLI, run the `npm stage approve ` command. + - On [npmjs.com](https://www.npmjs.com), review the staged package in the **Staged Packages** tab, then click **Approve**. + + + + **Note:** You will be prompted for 2FA verification regardless of whether you approve the package in the CLI or on [npmjs.com](https://www.npmjs.com). Once approved, the package is published to the live registry. + + + +For the full staged publishing workflow, including reviewing, inspecting, and rejecting staged packages, see [Staged publishing][staged-publishing]. + [scopes]: about-scopes [private-pkgs]: about-private-packages [user-signup]: https://www.npmjs.com/signup @@ -123,3 +162,4 @@ For more information on the `publish` command, see the [CLI documentation][cli-p [config-2fa]: /configuring-two-factor-authentication [creating-token]: /creating-and-viewing-access-tokens [requiring-2fa]: /requiring-2fa-for-package-publishing-and-settings-modification +[staged-publishing]: /staged-publishing diff --git a/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-scoped-public-packages.mdx b/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-scoped-public-packages.mdx index 8a46633c297..a0a29353648 100644 --- a/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-scoped-public-packages.mdx +++ b/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-scoped-public-packages.mdx @@ -77,16 +77,19 @@ npm install /path/to/my-test-package By default, scoped packages are published with private visibility. To publish a scoped package with public visibility, use `npm publish --access public`. - +There are two ways to publish your package to the npm registry: -**Important:** Publishing to npm requires either: +1. [Direct publishing](#direct-publishing) +2. [Staged publishing](#staged-publishing) -- Two-factor authentication (2FA) enabled on your account, OR -- A granular access token with bypass 2FA enabled +### Direct publishing -For more information, see the npm documentation on [requiring 2FA for package publishing](/requiring-2fa-for-package-publishing-and-settings-modification). +To publish directly with `npm publish --access public`, you need either: - +- Two-factor authentication (2FA) enabled on your account, or +- A granular access token (GAT) with bypass 2FA enabled + +For more information, see the npm documentation on [requiring 2FA for package publishing](/requiring-2fa-for-package-publishing-and-settings-modification). 1. On the command line, navigate to the root directory of your package. @@ -112,6 +115,42 @@ For more information, see the npm documentation on [requiring 2FA for package pu For more information on the `publish` command, see the [CLI documentation][cli-publish]. +### Staged publishing + +Instead of publishing directly, you can stage your package and approve it later. Staging the package does not require 2FA, which allows CI workflows to submit a package to the staging area. Before the package becomes publicly available, a maintainer must review and approve it with 2FA. + +A GAT with bypass 2FA does not bypass the 2FA check during staged package approval. + +1. On the command line, navigate to the root directory of your package. + + ``` + cd /path/to/my-test-package + ``` + +2. To stage your scoped public package, run: + + ``` + npm stage publish + ``` + + This submits your package to a staging area. + +3. To check that your package has been staged, use either of the following methods: + - In the CLI, run `npm stage list ` to find the staged package and its stage ID. + - On [npmjs.com](https://www.npmjs.com), open the **Staged Packages** tab to review staged packages. + +4. To approve and publish the staged package, use one of the following methods: + - In the CLI, run the `npm stage approve ` command. + - On [npmjs.com](https://www.npmjs.com), review the staged package in the **Staged Packages** tab, then click **Approve**. + + + + **Note:** You will be prompted for 2FA verification regardless of whether you approve the package in the CLI or on [npmjs.com](https://www.npmjs.com). Once approved, the package is published to the live registry. + + + +For the full staged publishing workflow, including reviewing, inspecting, and rejecting staged packages, see [Staged publishing][staged-publishing]. + [scopes]: /about-scopes [user-signup]: https://www.npmjs.com/signup [create-org]: https://www.npmjs.com/signup?next=/org/create @@ -125,3 +164,4 @@ For more information on the `publish` command, see the [CLI documentation][cli-p [config-2fa]: /configuring-two-factor-authentication [creating-token]: /creating-and-viewing-access-tokens [requiring-2fa]: /requiring-2fa-for-package-publishing-and-settings-modification +[staged-publishing]: /staged-publishing diff --git a/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-unscoped-public-packages.mdx b/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-unscoped-public-packages.mdx index 608b53385d7..bea4824c653 100644 --- a/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-unscoped-public-packages.mdx +++ b/content/packages-and-modules/contributing-packages-to-the-registry/creating-and-publishing-unscoped-public-packages.mdx @@ -58,16 +58,19 @@ npm install path/to/my-package ## Publishing unscoped public packages - +There are two ways to publish your package to the npm registry: -**Important:** Publishing to npm requires either: +1. [Direct publishing](#direct-publishing) +2. [Staged publishing](#staged-publishing) -- Two-factor authentication (2FA) enabled on your account, OR -- A granular access token with bypass 2FA enabled +### Direct publishing -For more information, see the npm documentation on [requiring 2FA for package publishing](/requiring-2fa-for-package-publishing-and-settings-modification). +To publish directly with `npm publish`, you need either: - +- Two-factor authentication (2FA) enabled on your account, or +- A granular access token (GAT) with bypass 2FA enabled + +For more information, see the npm documentation on [requiring 2FA for package publishing](/requiring-2fa-for-package-publishing-and-settings-modification). 1. On the command line, navigate to the root directory of your package. @@ -91,6 +94,42 @@ For more information, see the npm documentation on [requiring 2FA for package pu For more information on the `publish` command, see the [CLI documentation][cli-publish]. +### Staged publishing + +Instead of publishing directly, you can stage your package and approve it later. Staging the package does not require 2FA, which allows CI workflows to submit a package to the staging area. Before the package becomes publicly available, a maintainer must review and approve it with 2FA. + +A GAT with bypass 2FA does not bypass the 2FA check during staged package approval. + +1. On the command line, navigate to the root directory of your package. + + ``` + cd /path/to/package + ``` + +2. To stage your package, run: + + ``` + npm stage publish + ``` + + This submits your package to a staging area. + +3. To check that your package has been staged, use either of the following methods: + - In the CLI, run `npm stage list ` to find the staged package and its stage ID. + - On [npmjs.com](https://www.npmjs.com), open the **Staged Packages** tab to review staged packages. + +4. To approve and publish the staged package, use one of the following methods: + - In the CLI, run the `npm stage approve ` command. + - On [npmjs.com](https://www.npmjs.com), review the staged package in the **Staged Packages** tab, then click **Approve**. + + + + **Note:** You will be prompted for 2FA verification regardless of whether you approve the package in the CLI or on [npmjs.com](https://www.npmjs.com). Once approved, the package is published to the live registry. + + + +For the full staged publishing workflow, including reviewing, inspecting, and rejecting staged packages, see [Staged publishing][staged-publishing]. + [pkg-viz]: package-scope-access-level-and-visibility [user-signup]: https://www.npmjs.com/signup [create-org]: https://www.npmjs.com/signup?next=/org/create @@ -103,3 +142,4 @@ For more information on the `publish` command, see the [CLI documentation][cli-p [config-2fa]: /configuring-two-factor-authentication [creating-token]: /creating-and-viewing-access-tokens [requiring-2fa]: /requiring-2fa-for-package-publishing-and-settings-modification +[staged-publishing]: /staged-publishing diff --git a/content/packages-and-modules/securing-your-code/staged-publishing.mdx b/content/packages-and-modules/securing-your-code/staged-publishing.mdx new file mode 100644 index 00000000000..7f237b18e5e --- /dev/null +++ b/content/packages-and-modules/securing-your-code/staged-publishing.mdx @@ -0,0 +1,122 @@ +--- +title: Staged publishing for npm packages +--- + +Staged publishing adds an approval step before packages go live on the npm registry. Instead of publishing directly with `npm publish`, you can submit packages to a staging area with `npm stage publish`. A maintainer must then review and explicitly approve the staged package — with two-factor authentication (2FA) via the CLI or [npmjs.com](https://www.npmjs.com) — before it becomes publicly available. + +Staged publishing is useful when you want an extra review step before a package version becomes available on the registry. + + + +**Note:** Staged publishing requires [npm CLI](https://docs.npmjs.com/cli/v11) version 11.15.0 or later and Node version 22.14.0 or higher. + + + +## How staged publishing works + +Staged publishing has three steps: + +1. [Stage a package](#stage-a-package) +2. [Review a staged package](#review-a-staged-package) +3. [Approve a staged package](#approve-a-staged-package) + +## Prerequisites + +Before using staged publishing, ensure the following: + +- You have **publish access** to the package +- The package **already exists** on the npm registry — you cannot stage a brand-new package +- **2FA is enabled** on your npm account + +## Stage a package + +1. On the command line, navigate to the root directory of your package. + + ``` + cd /path/to/package + ``` + +2. To stage your package, run: + + ``` + npm stage publish + ``` + + This submits your package to a staging area. + + + +**Note:** `npm stage publish` does not require 2FA. + + + +## Review a staged package + +After you stage a package, you can inspect it in the CLI or on [npmjs.com](https://www.npmjs.com). + +#### Using the CLI + +To list staged packages you have access to: + +``` +npm stage list [] +``` + +To view details for a specific staged package: + +``` +npm stage view +``` + +To download the staged package tarball for inspection: + +``` +npm stage download +``` + +#### Using npmjs.com + +Open the **Staged Packages** tab to review staged packages and find the package you want to approve. + + + +## Approve a staged package + +To publish a staged package to the registry, approve it with 2FA. + +#### Using the CLI + +To approve a staged package and publish it to the live registry: + +``` +npm stage approve +``` + +#### Using npmjs.com + +On [npmjs.com](https://www.npmjs.com), review the staged package in the **Staged Packages** tab, then click **Approve**. + + + + + +**Note:** You will be prompted for 2FA verification whether you approve the package in the CLI or on [npmjs.com](https://www.npmjs.com). + + + +## Using staged publishing with trusted publishers + +If you use [trusted publishing (OIDC)](/trusted-publishers) from CI/CD, you can use staged publishing to submit a package for review before it goes live. A maintainer must still review and approve the staged package with 2FA. + +For more information on configuring trusted publisher permissions, see "[Trusted publishing for npm packages](/trusted-publishers#configuring-allowed-actions)." + +## Learn more + +- [Trusted publishing for npm packages](./trusted-publishers) +- [Generating provenance statements](./generating-provenance-statements) diff --git a/content/packages-and-modules/securing-your-code/trusted-publishers.mdx b/content/packages-and-modules/securing-your-code/trusted-publishers.mdx index 4c52609b4d5..b5497acc493 100644 --- a/content/packages-and-modules/securing-your-code/trusted-publishers.mdx +++ b/content/packages-and-modules/securing-your-code/trusted-publishers.mdx @@ -45,6 +45,7 @@ Configure the following fields: - Must include the `.yml` or `.yaml` extension - The workflow file must exist in `.github/workflows/` in your repository - **Environment name** (optional): If using [GitHub environments](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) for deployment protection +- **Allowed actions** (required): Select which actions this trusted publisher can perform — `npm publish`, `npm stage publish`, or both. At least one must be selected. See [Staged publishing for npm packages](/staged-publishing) for more information on staged publishing. @@ -57,6 +58,7 @@ Configure the following fields: - **Top-level CI file path** (required): The path to your CI file (e.g., `.gitlab-ci.yml`) - Must include the `.yml` extension - **Environment name** (optional): If using [GitLab environments](https://docs.gitlab.com/ee/ci/environments/) +- **Allowed actions** (required): Select which actions this trusted publisher can perform — `npm publish`, `npm stage publish`, or both. At least one must be selected. @@ -69,6 +71,7 @@ Configure the following fields: - **Pipeline definition ID** (required): The pipeline definition ID (UUID format). You may find it from your CircleCI Project Settings under Project Setup page. - **VCS origin** (required): The VCS origin URL for your project (e.g., `github.com/myorg/myrepo`) - **Context IDs** (optional): Restrict publishing to jobs using specific CircleCI contexts. You may find them from your CircleCI Organization Settings Contexts. +- **Allowed actions** (required): Select which actions this trusted publisher can perform — `npm publish`, `npm stage publish`, or both. At least one must be selected. @@ -78,6 +81,12 @@ Configure the following fields: + + +**Note:** Trusted publisher configurations created before May 20, 2026 are automatically set to allow `npm publish` only — no behavior change occurs for current workflows. Configurations created after May 20, 2026 require you to explicitly select at least one allowed action. + + + ### Step 2: Configure your CI/CD workflow #### GitHub Actions configuration @@ -110,7 +119,7 @@ jobs: - run: npm ci - run: npm run build --if-present - run: npm test - - run: npm publish + - run: npm publish # Or: npm stage publish ``` The critical requirement is the `id-token: write` permission, which allows GitHub Actions to generate OIDC tokens. Learn more in [GitHub's OIDC documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect). @@ -146,7 +155,7 @@ publish: script: - npm ci - npm run build --if-present - - npm publish + - npm publish # Or: npm stage publish only: - tags ``` @@ -185,7 +194,7 @@ jobs: name: Publish to npm with OIDC command: | export NPM_ID_TOKEN=$(circleci run oidc get --claims '{"aud": "npm:registry.npmjs.org"}') - npm publish + npm publish # Or: npm stage publish workflows: publish: @@ -218,6 +227,8 @@ Once you've configured trusted publishers for your package, we strongly recommen Trusted publishers use short-lived, scoped credentials that are generated on-demand during your CI/CD workflow, eliminating the need for long-lived tokens. By restricting traditional token access while using trusted publishers, you reduce potential security risks associated with credential management. +For even stronger security, configure your trusted publisher with **stage-only permissions** (allow `npm stage publish` but not `npm publish`). This ensures all CI-automated publishes go through the [staged publishing](/staged-publishing) flow, requiring a maintainer to review and approve each package with 2FA via the CLI or [npmjs.com](https://www.npmjs.com) before it becomes publicly available. Combined with disallowing tokens, this provides the maximum security posture for your packages. + **Note:** The "disallow tokens" setting only affects traditional token authentication. Your trusted publishers will continue to work normally, as they use OIDC tokens. ### Migration tip @@ -346,12 +357,13 @@ Some GitHub Actions workflows use `workflow_call` to invoke other workflows that Trusted publishing currently supports only cloud-hosted runners. Support for self-hosted runners is intended for a future release. Each package can only have one trusted publisher configured at a time, though you can update this configuration as needed. -OIDC authentication is currently limited to the publish operation. Other npm commands such as `install`, `view`, or `access` still require traditional authentication methods. The `npm whoami` command will not reflect OIDC authentication status since the authentication occurs only during the publish operation. +OIDC authentication supports the `npm publish` and `npm stage publish` commands. Other stage subcommands (`npm stage list`, `npm stage view`, `npm stage approve`, `npm stage reject`) require interactive authentication and cannot use OIDC tokens, as these actions require proof of presence and can only be performed via the CLI or [npmjs.com](https://www.npmjs.com). Other npm commands such as `install`, `view`, or `access` still require traditional authentication methods. The `npm whoami` command will not reflect OIDC authentication status since the authentication occurs only during the publish or stage operation. We intend to expand trusted publishing support to additional CI/CD providers and enhance the feature based on community feedback. ## Learn more +- [Staged publishing for npm packages](./staged-publishing) - [About npm provenance](./generating-provenance-statements) - [OpenSSF Trusted Publishers specification](https://repos.openssf.org/trusted-publishers-for-all-package-repositories) - [GitHub Actions OIDC documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) diff --git a/static/packages-and-modules/securing-your-code/staged-package-approve.png b/static/packages-and-modules/securing-your-code/staged-package-approve.png new file mode 100644 index 00000000000..20df38256e4 Binary files /dev/null and b/static/packages-and-modules/securing-your-code/staged-package-approve.png differ diff --git a/static/packages-and-modules/securing-your-code/staged-package-tab.png b/static/packages-and-modules/securing-your-code/staged-package-tab.png new file mode 100644 index 00000000000..93f7be2b4ac Binary files /dev/null and b/static/packages-and-modules/securing-your-code/staged-package-tab.png differ diff --git a/static/packages-and-modules/securing-your-code/trusted-publisher-circleci.png b/static/packages-and-modules/securing-your-code/trusted-publisher-circleci.png index a95f932abbf..cee586e724f 100644 Binary files a/static/packages-and-modules/securing-your-code/trusted-publisher-circleci.png and b/static/packages-and-modules/securing-your-code/trusted-publisher-circleci.png differ diff --git a/static/packages-and-modules/securing-your-code/trusted-publisher-github-actions.png b/static/packages-and-modules/securing-your-code/trusted-publisher-github-actions.png index b24f48e030a..1a56967aea2 100644 Binary files a/static/packages-and-modules/securing-your-code/trusted-publisher-github-actions.png and b/static/packages-and-modules/securing-your-code/trusted-publisher-github-actions.png differ diff --git a/static/packages-and-modules/securing-your-code/trusted-publisher-gitlab.png b/static/packages-and-modules/securing-your-code/trusted-publisher-gitlab.png index d21b9c24395..07c06abedf5 100644 Binary files a/static/packages-and-modules/securing-your-code/trusted-publisher-gitlab.png and b/static/packages-and-modules/securing-your-code/trusted-publisher-gitlab.png differ