1- import { cliux , CLIError , log , cliErrorHandler } from '@contentstack/cli-utilities' ;
1+ import { cliux , log , handleAndLogError , messageHandler } from '@contentstack/cli-utilities' ;
22import { User } from '../interfaces' ;
33import { askOTPChannel , askOTP } from './interactive' ;
44
@@ -27,9 +27,61 @@ class AuthHandler {
2727 * @returns {Promise } Promise object returns authtoken on success
2828 * TBD: take out the otp implementation from login and create a new method/function to handle otp
2929 */
30+ /**
31+ * Handle the OTP flow for 2FA authentication
32+ * @param tfaToken Optional pre-provided TFA token
33+ * @param loginPayload Login payload containing user credentials
34+ * @returns Promise<string> The TFA token to use for authentication
35+ */
36+ private async handleOTPFlow ( tfaToken ?: string , loginPayload ?: any ) : Promise < string > {
37+ try {
38+ if ( tfaToken ) {
39+ log . info ( 'Using provided TFA token' , { module : 'auth-handler' } ) ;
40+ return tfaToken ;
41+ }
42+
43+ log . debug ( '2FA required, requesting OTP channel' , { module : 'auth-handler' } ) ;
44+ const otpChannel = await askOTPChannel ( ) ;
45+ log . debug ( `OTP channel selected: ${ otpChannel } ` , { module : 'auth-handler' } ) ;
46+
47+ if ( otpChannel === 'sms' ) {
48+ try {
49+ await this . requestSMSOTP ( loginPayload ) ;
50+ } catch ( error ) {
51+ log . debug ( 'SMS OTP request failed' , { module : 'auth-handler' , error } ) ;
52+ cliux . print ( 'CLI_AUTH_SMS_OTP_FAILED' , { color : 'red' } ) ;
53+ throw error ;
54+ }
55+ }
56+
57+ log . debug ( 'Requesting OTP input' , { module : 'auth-handler' , channel : otpChannel } ) ;
58+ return await askOTP ( ) ;
59+ } catch ( error ) {
60+ log . debug ( '2FA flow failed' , { module : 'auth-handler' , error } ) ;
61+ throw error ;
62+ }
63+ }
64+
65+ /**
66+ * Request SMS OTP for 2FA authentication
67+ * @param loginPayload Login payload containing user credentials
68+ * @throws CLIError if SMS request fails
69+ */
70+ private async requestSMSOTP ( loginPayload : any ) : Promise < void > {
71+ log . debug ( 'Sending SMS OTP request' , { module : 'auth-handler' } ) ;
72+ try {
73+ await this . _client . axiosInstance . post ( '/user/request_token_sms' , { user : loginPayload } ) ;
74+ log . debug ( 'SMS OTP request successful' , { module : 'auth-handler' } ) ;
75+ cliux . print ( 'CLI_AUTH_LOGIN_SECURITY_CODE_SEND_SUCCESS' ) ;
76+ } catch ( error ) {
77+ log . debug ( 'SMS OTP request failed' , { module : 'auth-handler' , error } ) ;
78+ throw error ;
79+ }
80+ }
81+
3082 async login ( email : string , password : string , tfaToken ?: string ) : Promise < User > {
31- const hasCredentials = ! ! password ;
32- const hasTfaToken = ! ! tfaToken ;
83+ const hasCredentials = typeof password === 'string' && password . length > 0 ;
84+ const hasTfaToken = typeof tfaToken === 'string' && tfaToken . length > 0 ;
3385 log . debug ( 'Starting login process' , {
3486 module : 'auth-handler' ,
3587 email,
@@ -49,11 +101,9 @@ class AuthHandler {
49101 log . debug ( 'Adding TFA token to login payload' , { module : 'auth-handler' } ) ;
50102 }
51103
52- const hasCredentials = ! ! password ;
53- const hasTfaTokenPresent = ! ! tfaToken ;
54104 log . debug ( 'Making login API call' , {
55105 module : 'auth-handler' ,
56- payload : { email, hasCredentials, hasTfaTokenPresent } ,
106+ payload : { email, hasCredentials, hasTfaToken } ,
57107 } ) ;
58108
59109 this . _client
@@ -69,46 +119,24 @@ class AuthHandler {
69119 log . debug ( 'Login successful, user found' , { module : 'auth-handler' , userEmail : result . user . email } ) ;
70120 resolve ( result . user as User ) ;
71121 } else if ( result . error_code === 294 ) {
72- log . debug ( 'TFA required, requesting OTP channel' , { module : 'auth-handler' } ) ;
73- const otpChannel = await askOTPChannel ( ) ;
74- log . debug ( `OTP channel selected: ${ otpChannel } ` , { module : 'auth-handler' } ) ;
75-
76- // need to send sms to the mobile
77- if ( otpChannel === 'sms' ) {
78- log . debug ( 'Sending SMS OTP request' , { module : 'auth-handler' } ) ;
79- try {
80- await this . _client . axiosInstance . post ( '/user/request_token_sms' , { user : loginPayload } ) ;
81- log . debug ( 'SMS OTP request successful' , { module : 'auth-handler' } ) ;
82- cliux . print ( 'CLI_AUTH_LOGIN_SECURITY_CODE_SEND_SUCCESS' ) ;
83- } catch ( error ) {
84- log . debug ( 'SMS OTP request failed' , { module : 'auth-handler' , error } ) ;
85- const err = cliErrorHandler . classifyError ( error ) ;
86- reject ( err ) ;
87- return ;
88- }
89- }
90-
91- log . debug ( 'Requesting OTP input from user' , { module : 'auth-handler' } ) ;
92- const tfToken = await askOTP ( ) ;
93- log . debug ( 'OTP received, retrying login' , { module : 'auth-handler' } ) ;
122+ const tfToken = await this . handleOTPFlow ( tfaToken , loginPayload ) ;
94123
95124 try {
96125 resolve ( await this . login ( email , password , tfToken ) ) ;
97126 } catch ( error ) {
98127 log . debug ( 'Login with TFA token failed' , { module : 'auth-handler' , error } ) ;
99- const err = cliErrorHandler . classifyError ( error ) ;
100- reject ( err ) ;
101- return ;
128+ cliux . print ( 'CLI_AUTH_2FA_FAILED' , { color : 'red' } ) ;
129+ reject ( error ) ;
102130 }
103131 } else {
104132 log . debug ( 'Login failed - no user found' , { module : 'auth-handler' , result } ) ;
105- reject ( new CLIError ( { message : 'No user found with the credentials' } ) ) ;
133+ reject ( new Error ( messageHandler . parse ( 'CLI_AUTH_LOGIN_NO_USER' ) ) ) ;
106134 }
107135 } )
108136 . catch ( ( error : any ) => {
109- log . debug ( 'Login API call failed' , { module : 'auth-handler' , error : error . message || error } ) ;
110- const err = cliErrorHandler . classifyError ( error ) ;
111- reject ( err ) ;
137+ log . debug ( 'Login API call failed' , { module : 'auth-handler' , error : error ?. errorMessage || error } ) ;
138+ cliux . print ( 'CLI_AUTH_LOGIN_FAILED' , { color : 'yellow' } ) ;
139+ handleAndLogError ( error , { module : 'auth-handler' } ) ;
112140 } ) ;
113141 } else {
114142 const hasEmail = ! ! email ;
@@ -118,7 +146,8 @@ class AuthHandler {
118146 hasEmail,
119147 hasCredentials,
120148 } ) ;
121- reject ( new CLIError ( { message : 'No credential found to login' } ) ) ;
149+ log . debug ( 'Login failed - missing credentials' , { module : 'auth-handler' , hasEmail, hasCredentials } ) ;
150+ reject ( new Error ( messageHandler . parse ( 'CLI_AUTH_LOGIN_NO_CREDENTIALS' ) ) ) ;
122151 }
123152 } ) ;
124153 }
@@ -143,12 +172,12 @@ class AuthHandler {
143172 } )
144173 . catch ( ( error : Error ) => {
145174 log . debug ( 'Logout API call failed' , { module : 'auth-handler' , error : error . message } ) ;
146- const err = cliErrorHandler . classifyError ( error ) ;
147- reject ( err ) ;
175+ cliux . print ( 'CLI_AUTH_LOGOUT_FAILED' , { color : 'yellow' } ) ;
176+ reject ( error ) ;
148177 } ) ;
149178 } else {
150179 log . debug ( 'Logout failed - no auth token provided' , { module : 'auth-handler' } ) ;
151- reject ( new CLIError ( { message : 'No auth token found to logout' } ) ) ;
180+ reject ( new Error ( messageHandler . parse ( 'CLI_AUTH_LOGOUT_NO_TOKEN' ) ) ) ;
152181 }
153182 } ) ;
154183 }
@@ -173,12 +202,12 @@ class AuthHandler {
173202 } )
174203 . catch ( ( error : Error ) => {
175204 log . debug ( 'Token validation failed' , { module : 'auth-handler' , error : error . message } ) ;
176- const err = cliErrorHandler . classifyError ( error ) ;
177- reject ( err ) ;
205+ cliux . print ( 'CLI_AUTH_TOKEN_VALIDATION_FAILED' , { color : 'yellow' } ) ;
206+ handleAndLogError ( error , { module : 'auth-handler' } ) ;
178207 } ) ;
179208 } else {
180209 log . debug ( 'Token validation failed - no auth token provided' , { module : 'auth-handler' } ) ;
181- reject ( new CLIError ( { message : 'No auth token found to validate' } ) ) ;
210+ reject ( new Error ( messageHandler . parse ( 'CLI_AUTH_TOKEN_VALIDATION_NO_TOKEN' ) ) ) ;
182211 }
183212 } ) ;
184213 }
0 commit comments