Skip to content
Open
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
38 changes: 38 additions & 0 deletions api/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,35 @@ tags:
- `/oidc/token/exchange` endpoint: Only accepts `oidcIdToken`
- Package publishing: May accept `npmSessionToken`, `npmAccessToken`, or `oidcExchangeToken`

**Granular Access Tokens with `bypass_2fa`:**

Granular Access Tokens (GATs) created with `bypass_2fa: true` are
allowed to perform automation flows such as direct publish, but they
are **not** permitted to perform account-identity or governance write
operations. Requests to those endpoints authenticated with a
bypass-2FA GAT are rejected with `403 Forbidden` and must instead be
performed via an interactive 2FA challenge (npm website, or an npm
session token / non-bypass GAT with a valid `npm-otp`).

Operations rejected for bypass-2FA GATs include (non-exhaustive):

- Token management — `POST /-/npm/v1/tokens`, `DELETE /-/npm/v1/tokens/token/{token}`
- Account settings — change password, change email, change 2FA
configuration, generate recovery codes, update profile
- Package governance — `POST /-/package/{escapedPackageName}/access`,
`POST /-/package/{package}/trust`,
`DELETE /-/package/{package}/trust/{config-uuid}`,
maintainer edits, and package/binary deletion (unpublish)
- Org and team governance —
`PUT`/`DELETE /-/org/{orgName}/user`,
`PUT /-/org/{orgName}/team`,
`DELETE /-/org/{orgName}/{teamName}`,
`PUT`/`DELETE /-/org/{orgName}/{teamName}/user`,
`PUT`/`DELETE /-/team/{orgName}/{teamName}/package`

Direct publish (`PUT /{escapedPackageName}`) remains functional for
bypass-2FA GATs.

- name: Tokens
x-displayName: Tokens
description: |
Expand Down Expand Up @@ -131,6 +160,11 @@ components:
Granular Access Token (GAT) with fine-grained permissions.
These tokens can be scoped to specific packages and operations.

GATs created with `bypass_2fa: true` cannot perform account,
token, or org/team/package governance write operations — those
requests are rejected with `403 Forbidden`. See the Authentication
section above for the full list.

# Alias for npmAccessToken - for backward compatibility
granularAccessToken:
type: http
Expand All @@ -140,6 +174,10 @@ components:
These tokens can be scoped to specific packages and operations.
(Alias for npmAccessToken)

GATs created with `bypass_2fa: true` cannot perform account,
token, or org/team/package governance write operations — those
requests are rejected with `403 Forbidden`.

# Short-lived tokens obtained from OIDC token exchange
oidcExchangeToken:
type: http
Expand Down
30 changes: 30 additions & 0 deletions api/registry.npmjs.com/access.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ paths:
tags:
- Access
summary: Grant access to a package for a team
description: |
Grant a team access to a package.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: createTeamPackageGrant
requestBody:
required: true
Expand All @@ -75,23 +81,37 @@ paths:
description: The access level of the package to grant to the team
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'201':
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
delete:
tags:
- Access
summary: Remove access to a package for a team
description: |
Remove a team's access to a package.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: deleteTeamPackageGrant
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'204':
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
/-/org/{orgName}/package:
parameters:
- $ref: './api/shared-components.yaml#/components/parameters/OrgName'
Expand Down Expand Up @@ -148,6 +168,12 @@ paths:
tags:
- Access
summary: Sets the various access levels for a package.
description: |
Set package visibility and 2FA/automation policy for a package.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: setPackageAccess
requestBody:
required: true
Expand All @@ -171,8 +197,12 @@ paths:
description: Whether or not automation tokens override the requirement for multifactor auth
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'200':
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
22 changes: 20 additions & 2 deletions api/registry.npmjs.com/org.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,17 @@ paths:
tags:
- Org
summary: Set user membership in an org
description: Set a user's membership in an org. If the user is not already a member, an invite will be sent.
description: |
Set a user's membership in an org. If the user is not already a member, an invite will be sent.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: changeOrgMembership
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
requestBody:
required: true
content:
Expand All @@ -86,14 +93,23 @@ paths:
$ref: '#/components/responses/OrgInvite'
"401":
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
"403":
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
delete:
tags:
- Org
summary: Remove user membership in an org
description: Remove a user's membership in an org
description: |
Remove a user's membership in an org.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: deleteOrgMembership
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
requestBody:
required: true
content:
Expand All @@ -109,3 +125,5 @@ paths:
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
"401":
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
"403":
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
43 changes: 40 additions & 3 deletions api/registry.npmjs.com/team.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ paths:
tags:
- Team
summary: Create a new team
description: Create a new team for an org
description: |
Create a new team for an org.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: putScopeTeam
requestBody:
required: true
Expand All @@ -64,6 +69,8 @@ paths:
example: { name: 'wombats', description: 'All developers' }
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'201':
description: Team was created successfully
Expand All @@ -79,6 +86,8 @@ paths:
name: wombats
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
/-/org/{orgName}/{teamName}:
parameters:
- $ref: './api/shared-components.yaml#/components/parameters/OrgName'
Expand All @@ -88,15 +97,24 @@ paths:
tags:
- Team
summary: Delete a team
description: Delete a team from a given org
description: |
Delete a team from a given org.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: deleteTeam
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'204':
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
/-/org/{orgName}/{teamName}/user:
parameters:
- $ref: './api/shared-components.yaml#/components/parameters/OrgName'
Expand All @@ -119,7 +137,12 @@ paths:
tags:
- Team
summary: Add a user to a team
description: Add a user to a team in an org. The user must already be a member of the org.
description: |
Add a user to a team in an org. The user must already be a member of the org.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: createTeamMembership
requestBody:
required: true
Expand All @@ -135,15 +158,25 @@ paths:
user: npm-cli-bot
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'201':
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
delete:
tags:
- Team
summary: Remove a user from a team
description: |
Remove a user from a team.

Granular Access Tokens with `bypass_2fa: true` are not permitted to
call this endpoint and receive `403 Forbidden`. This operation must
be performed via an interactive 2FA challenge.
operationId: deleteTeamMembership
requestBody:
required: true
Expand All @@ -159,8 +192,12 @@ paths:
user: npm-cli-bot
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
'204':
$ref: './api/shared-components.yaml#/components/responses/EmptySuccess'
'401':
$ref: './api/shared-components.yaml#/components/responses/Unauthorized'
'403':
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
40 changes: 37 additions & 3 deletions api/registry.npmjs.com/token.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ paths:
- If 2FA is enabled on your account, provide the OTP via the `npm-otp` header
- If 2FA is not enabled, an email OTP will be sent and must be provided via the `npm-otp` header
- For WebAuthn users, the OTP is returned in the `doneUrl` after authentication
- **Granular Access Tokens with `bypass_2fa: true` are not permitted**
to call this endpoint and receive `403 Forbidden`. Token creation
(and escalation) requires an interactive 2FA challenge.

**Important notices:**
- All responses include security notices via the `npm-notice` header regarding token limitations
Expand Down Expand Up @@ -86,7 +89,18 @@ paths:
description: 'Expiration in days (number) or ISO date string. Read-write tokens: maximum 90 days, defaults to 7 days. Read-only tokens: unlimited maximum, defaults to 30 days'
bypass_2fa:
type: boolean
description: Allow token to bypass 2FA requirements
description: |
Allow token to bypass 2FA requirements for automation
flows such as direct publish.

Tokens created with `bypass_2fa: true` **cannot** perform
account, token, or org/team/package governance write
operations (for example: creating or deleting tokens,
changing package access or trust configuration,
adding/removing org or team members, granting/revoking
team package access, unpublishing). Those requests are
rejected with `403 Forbidden` and must be performed via
an interactive 2FA challenge.
default: false
cidr:
type: array
Expand Down Expand Up @@ -122,6 +136,8 @@ paths:
- name
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
"201":
description: "Token created successfully"
Expand Down Expand Up @@ -165,7 +181,11 @@ paths:
nullable: true
bypass_2fa:
type: boolean
description: Indicates if the token can bypass 2FA requirements.
description: |
Indicates if the token can bypass 2FA requirements for
automation flows. When `true`, the token is rejected
(`403 Forbidden`) by account, token, and org/team/package
governance write endpoints.
revoked:
type: string
format: date-time
Expand Down Expand Up @@ -265,6 +285,8 @@ paths:
error: 'Read-write tokens cannot have expiration longer than 90 days'
"401":
$ref: '#/components/responses/UnauthorizedWithWebAuth'
"403":
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
Comment thread
shmam marked this conversation as resolved.

"500":
description: Internal server error
Expand Down Expand Up @@ -359,7 +381,12 @@ paths:
description: Indicates if the token has readonly permissions
bypass_2fa:
type: boolean
description: Indicates if the token can bypass 2FA requirements
description: |
Indicates if the token can bypass 2FA
requirements for automation flows. When `true`,
the token is rejected (`403 Forbidden`) by
account, token, and org/team/package governance
write endpoints.
cidr:
type: array
items:
Expand Down Expand Up @@ -457,6 +484,9 @@ paths:
**Requirements:**
- Must be authenticated with a valid Bearer token
- May require 2FA OTP depending on user settings
- **Granular Access Tokens with `bypass_2fa: true` are not permitted**
to call this endpoint and receive `403 Forbidden`. Token deletion
requires an interactive 2FA challenge.

**Web authentication flow:**
- When `npm-auth-type=web` and `npm-command=token` headers are present and 2FA is required,
Expand Down Expand Up @@ -509,6 +539,8 @@ paths:
this is a token deletion command.
security:
- npmSessionToken: []
- npmAccessToken: []
- granularAccessToken: []
responses:
"204":
description: Token deleted successfully
Expand Down Expand Up @@ -586,6 +618,8 @@ paths:
value:
authUrl: "https://www.npmjs.com/auth/cli/00000000-0000-0000-0000-000000000000"
doneUrl: "https://registry.npmjs.org/-/v1/done?authId=00000000-0000-0000-0000-000000000000"
"403":
$ref: './api/shared-components.yaml#/components/responses/Forbidden2FABypassGAT'
"404":
description: Not found - Token does not exist
content:
Expand Down
Loading
Loading