11const { log, output } = require ( 'proc-log' )
2- const { listTokens, createToken , removeToken } = require ( 'npm-profile' )
2+ const { listTokens, createGatToken , removeToken } = require ( 'npm-profile' )
33const { otplease } = require ( '../utils/auth.js' )
44const readUserInfo = require ( '../utils/read-user-info.js' )
55const BaseCommand = require ( '../base-cmd.js' )
66
77class Token extends BaseCommand {
88 static description = 'Manage your authentication tokens'
99 static name = 'token'
10- static usage = [ 'list' , 'revoke <id|token>' , 'create [--read-only] [--cidr=list]' ]
11- static params = [ 'read-only' , 'cidr' , 'registry' , 'otp' ]
10+ static usage = [ 'list' , 'revoke <id|token>' , 'create --name=<name> --access=<read-only|read-write> [--expires=<YYYY-MM-DD>] [--packages=<pkg1,pkg2>] [--scopes=<scope1,scope2>] [--orgs=<org1,org2>] [--cidr=<ip-range>] [--bypass-2fa]' ]
11+ static params = [ 'name' ,
12+ 'expires' ,
13+ 'access' ,
14+ 'packages' ,
15+ 'scopes' ,
16+ 'orgs' ,
17+ 'cidr' ,
18+ 'bypass-2fa' ,
19+ 'registry' ,
20+ 'otp' ,
21+ 'read-only' ,
22+ ]
1223
1324 static async completion ( opts ) {
1425 const argv = opts . conf . argv . remain
@@ -127,15 +138,66 @@ class Token extends BaseCommand {
127138 const json = this . npm . config . get ( 'json' )
128139 const parseable = this . npm . config . get ( 'parseable' )
129140 const cidr = this . npm . config . get ( 'cidr' )
130- const readonly = this . npm . config . get ( 'read-only' )
141+ const name = this . npm . config . get ( 'name' )
142+ const expires = this . npm . config . get ( 'expires' )
143+ const access = this . npm . config . get ( 'access' )
144+ const packages = this . npm . config . get ( 'packages' )
145+ const scopes = this . npm . config . get ( 'scopes' )
146+ const orgs = this . npm . config . get ( 'orgs' )
147+ const bypassTwoFactor = this . npm . config . get ( 'bypass-2fa' )
148+
149+ // Validate required parameters
150+ if ( ! name ) {
151+ throw this . usageError ( '--name is required for token creation' )
152+ }
153+ if ( ! access ) {
154+ throw this . usageError ( '--access is required (use "read-only" or "read-write")' )
155+ }
156+ if ( ! [ 'read-only' , 'read-write' ] . includes ( access ) ) {
157+ throw this . usageError ( '--access must be either "read-only" or "read-write"' )
158+ }
131159
132160 const validCIDR = await this . validateCIDRList ( cidr )
161+
162+ // Prompt for password (required by backend for token creation)
133163 const password = await readUserInfo . password ( )
164+
165+ // Build GAT token data structure matching backend expectations
166+ const tokenData = {
167+ token_type : 'granular' ,
168+ name : name ,
169+ access : access ,
170+ password : password ,
171+ }
172+
173+ // Add packages, scopes, and orgs as separate arrays (not nested objects)
174+ if ( packages ?. length > 0 ) {
175+ tokenData . packages = packages
176+ }
177+ if ( scopes ?. length > 0 ) {
178+ tokenData . scopes = scopes
179+ }
180+ if ( orgs ?. length > 0 ) {
181+ tokenData . orgs = orgs
182+ }
183+ if ( expires ) {
184+ tokenData . expires = 10 // Hardcoding for now. Backend expects # of days as an integer
185+ }
186+
187+ // Add optional fields
188+ if ( validCIDR ?. length > 0 ) {
189+ tokenData . cidr_whitelist = validCIDR
190+ }
191+ if ( bypassTwoFactor ) {
192+ tokenData . bypass_2fa = true
193+ }
194+
134195 log . info ( 'token' , 'creating' )
196+ log . silly ( 'token' , 'request body:' , JSON . stringify ( tokenData , null , 2 ) )
135197 const result = await otplease (
136198 this . npm ,
137199 { ...this . npm . flatOptions } ,
138- c => createToken ( password , readonly , validCIDR , c )
200+ c => createGatToken ( tokenData , c )
139201 )
140202 delete result . key
141203 delete result . updated
@@ -145,12 +207,15 @@ class Token extends BaseCommand {
145207 Object . keys ( result ) . forEach ( k => output . standard ( k + '\t' + result [ k ] ) )
146208 } else {
147209 const chalk = this . npm . chalk
148- // Identical to list
149- const level = result . readonly ? 'read only' : 'publish'
210+ // Display based on access level
211+ const level = result . access === 'read-only' || result . readonly ? 'read only' : 'publish'
150212 output . standard ( `Created ${ chalk . blue ( level ) } token ${ result . token } ` )
151213 if ( result . cidr_whitelist ?. length ) {
152214 output . standard ( `with IP whitelist: ${ chalk . green ( result . cidr_whitelist . join ( ',' ) ) } ` )
153215 }
216+ if ( result . expires ) {
217+ output . standard ( `expires: ${ result . expires } ` )
218+ }
154219 }
155220 }
156221
@@ -180,7 +245,7 @@ class Token extends BaseCommand {
180245 for ( const cidr of list ) {
181246 if ( isCidrV6 ( cidr ) ) {
182247 throw this . invalidCIDRError (
183- `CIDR whitelist can only contain IPv4 addresses${ cidr } is IPv6`
248+ `CIDR whitelist can only contain IPv4 addresses, ${ cidr } is IPv6`
184249 )
185250 }
186251
0 commit comments