@@ -12,18 +12,22 @@ export default class AccountLogin extends ScCommand<typeof AccountLogin> {
1212Stores organization credentials securely using OS keychain.
1313The access token is encrypted and stored locally.
1414
15+ You can store multiple access tokens for the same organization by using unique aliases.
16+ Without an alias, only one token per organization ID is allowed.
17+
1518Required token permissions: Varies by operations you intend to perform`
1619 static override examples = [
1720 '<%= config.bin %> <%= command.id %> --org=my-org-id' ,
1821 '<%= config.bin %> <%= command.id %> --org=my-org-id --alias=production' ,
22+ '<%= config.bin %> <%= command.id %> --org=my-org-id --alias=staging' ,
1923 '<%= config.bin %> <%= command.id %> --org=my-org-id --set-default' ,
2024 '<%= config.bin %> <%= command.id %> --org=my-org-id --no-prompt' ,
2125 '<%= config.bin %> <%= command.id %> --org=my-org-id --base-url=https://api.custom.solace.cloud' ,
2226 ]
2327 static override flags = {
2428 'alias' : Flags . string ( {
2529 char : 'a' ,
26- description : 'Alias name for this organization (optional )' ,
30+ description : 'Alias name for this organization (allows storing multiple tokens for the same org with different aliases )' ,
2731 } ) ,
2832 'api-version' : Flags . string ( {
2933 description : 'API version to use (optional)' ,
@@ -51,105 +55,142 @@ Required token permissions: Varies by operations you intend to perform`
5155 const { flags} = await this . parse ( AccountLogin )
5256
5357 try {
54- // Get OrgManager instance
5558 const orgManager : OrgManager = await this . getOrgManager ( )
59+ const identifier = flags . alias ?? flags . org
5660
57- // Check if organization already exists
58- const orgExists = await orgManager . orgExists ( flags . org )
59- let isUpdate = false
60-
61- if ( orgExists ) {
62- // Prompt for overwrite confirmation
63- const shouldOverwrite = await this . promptForConfirmation (
64- `Organization '${ flags . org } ' already exists. Do you want to overwrite the access token?` ,
65- )
66-
67- if ( ! shouldOverwrite ) {
68- this . log ( 'Login cancelled.' )
69- this . exit ( 0 )
70- }
71-
72- // Remove existing organization to allow overwrite
73- await orgManager . removeOrg ( flags . org )
74- isUpdate = true
75- }
61+ // Handle existing organization/alias
62+ const isUpdate = await this . handleExistingEntry ( orgManager , identifier , flags . alias )
7663
7764 // Obtain access token
78- let accessToken : string
79-
80- if ( flags [ 'no-prompt' ] ) {
81- // Read from environment variable
82- accessToken = process . env . SC_ACCESS_TOKEN || ''
83- if ( ! accessToken ) {
84- this . error ( 'SC_ACCESS_TOKEN environment variable is not set. Please set it or remove the --no-prompt flag.' )
85- }
86- } else {
87- // Prompt for token
88- accessToken = await this . promptForToken ( )
89- }
90-
91- // Create OrgConfig object
92- const orgConfig : OrgConfig = {
93- accessToken,
94- alias : flags . alias ,
95- apiVersion : flags [ 'api-version' ] ,
96- baseUrl : flags [ 'base-url' ] ?? DEFAULT_BASE_URL ,
97- orgId : flags . org ,
98- }
65+ const accessToken = await this . obtainAccessToken ( flags [ 'no-prompt' ] )
9966
100- // Store organization
67+ // Create and store organization config
68+ const orgConfig = this . createOrgConfig ( flags , accessToken )
10169 await orgManager . addOrg ( orgConfig )
10270
10371 // Set as default if requested
10472 if ( flags [ 'set-default' ] ) {
105- await orgManager . setDefaultOrg ( flags . org )
73+ await orgManager . setDefaultOrg ( identifier )
10674 }
10775
10876 // Display success message
109- const action = isUpdate ? 'updated' : 'logged in to'
110- const aliasText = flags . alias ? ` (${ flags . alias } )` : ''
111- this . log ( `Successfully ${ action } organization '${ flags . org } '${ aliasText } ` )
112-
113- if ( flags [ 'set-default' ] ) {
114- this . log ( 'Set as default organization.' )
115- }
77+ this . displaySuccessMessage ( isUpdate , flags . org , flags . alias , flags [ 'set-default' ] )
11678
117- // Return for --json support
11879 return orgConfig
11980 } catch ( error ) {
120- // Handle OrgManager errors
121- if ( error instanceof OrgError ) {
122- switch ( error . code ) {
123- case OrgErrorCode . FILE_WRITE_ERROR : {
124- this . error ( 'Failed to save credentials. Please check file permissions.' )
125- break
126- }
81+ this . handleLoginError ( error )
82+ }
83+ }
12784
128- case OrgErrorCode . INVALID_ACCESS_TOKEN : {
129- this . error ( 'Invalid access token format. Please check your token and try again.' )
130- break
131- }
85+ /**
86+ * Creates organization configuration object
87+ */
88+ private createOrgConfig (
89+ flags : {
90+ alias ?: string
91+ 'api-version' ?: string
92+ 'base-url' ?: string
93+ org : string
94+ } ,
95+ accessToken : string ,
96+ ) : OrgConfig {
97+ return {
98+ accessToken,
99+ alias : flags . alias ,
100+ apiVersion : flags [ 'api-version' ] ,
101+ baseUrl : flags [ 'base-url' ] ?? DEFAULT_BASE_URL ,
102+ orgId : flags . org ,
103+ }
104+ }
132105
133- case OrgErrorCode . INVALID_ORG_ID : {
134- this . error ( 'Invalid organization ID format. Please check and try again.' )
135- break
136- }
106+ /**
107+ * Displays success message after login
108+ */
109+ private displaySuccessMessage ( isUpdate : boolean , orgId : string , alias ?: string , setDefault ?: boolean ) : void {
110+ const action = isUpdate ? 'updated' : 'logged in to'
111+ const aliasText = alias ? ` (${ alias } )` : ''
112+ this . log ( `Successfully ${ action } organization '${ orgId } '${ aliasText } ` )
137113
138- default : {
139- this . error ( `Login failed: ${ error . message } ` )
140- break
141- }
114+ if ( setDefault ) {
115+ this . log ( 'Set as default organization.' )
116+ }
117+ }
118+
119+ /**
120+ * Checks if entry exists and handles overwrite confirmation
121+ */
122+ private async handleExistingEntry ( orgManager : OrgManager , identifier : string , alias ?: string ) : Promise < boolean > {
123+ const exists = await orgManager . orgExists ( identifier )
124+ if ( ! exists ) {
125+ return false
126+ }
127+
128+ const identifierType = alias ? 'alias' : 'organization'
129+ const identifierDisplay = alias ? `alias '${ alias } '` : `organization '${ identifier } '`
130+
131+ const shouldOverwrite = await this . promptForConfirmation (
132+ `${ identifierType === 'alias' ? 'An entry with' : 'Organization' } ${ identifierDisplay } already exists. Do you want to overwrite the access token?` ,
133+ )
134+
135+ if ( ! shouldOverwrite ) {
136+ this . log ( 'Login cancelled.' )
137+ this . exit ( 0 )
138+ }
139+
140+ await orgManager . removeOrg ( identifier )
141+ return true
142+ }
143+
144+ /**
145+ * Handles errors during login process
146+ */
147+ private handleLoginError ( error : unknown ) : never {
148+ if ( error instanceof OrgError ) {
149+ switch ( error . code ) {
150+ case OrgErrorCode . FILE_WRITE_ERROR : {
151+ this . error ( 'Failed to save credentials. Please check file permissions.' )
152+ break
153+ }
154+
155+ case OrgErrorCode . INVALID_ACCESS_TOKEN : {
156+ this . error ( 'Invalid access token format. Please check your token and try again.' )
157+ break
158+ }
159+
160+ case OrgErrorCode . INVALID_ORG_ID : {
161+ this . error ( 'Invalid organization ID format. Please check and try again.' )
162+ break
163+ }
164+
165+ default : {
166+ this . error ( `Login failed: ${ error . message } ` )
167+ break
142168 }
143169 }
170+ }
144171
145- // Handle user cancellation
146- if ( error instanceof Error && error . message === 'Cancelled by user' ) {
147- this . error ( 'Login cancelled.' )
172+ if ( error instanceof Error && error . message === 'Cancelled by user' ) {
173+ this . error ( 'Login cancelled.' )
174+ }
175+
176+ // Re-throw unexpected errors
177+ throw error
178+ }
179+
180+ /**
181+ * Obtains access token from environment or user prompt
182+ */
183+ private async obtainAccessToken ( noPrompt : boolean ) : Promise < string > {
184+ if ( noPrompt ) {
185+ const accessToken = process . env . SC_ACCESS_TOKEN || ''
186+ if ( ! accessToken ) {
187+ this . error ( 'SC_ACCESS_TOKEN environment variable is not set. Please set it or remove the --no-prompt flag.' )
148188 }
149189
150- // Re-throw unexpected errors
151- throw error
190+ return accessToken
152191 }
192+
193+ return this . promptForToken ( )
153194 }
154195
155196 /**
0 commit comments