@@ -49,6 +49,81 @@ func findSessionCookie(cookies []*http.Cookie) *http.Cookie {
4949 return nil
5050}
5151
52+ func getLoginResponse (responseBody []byte ) (map [string ]interface {}, error ) {
53+ var responseMap map [string ]interface {}
54+ err := json .Unmarshal (responseBody , & responseMap )
55+ if err != nil {
56+ return nil , errors .New ("failed to parse login response: " + err .Error ())
57+ }
58+ loginRespRaw , ok := responseMap ["loginresponse" ]
59+ if ! ok {
60+ return nil , errors .New ("failed to parse login response, expected 'loginresponse' key not found" )
61+ }
62+ loginResponse , ok := loginRespRaw .(map [string ]interface {})
63+ if ! ok {
64+ return nil , errors .New ("failed to parse login response, expected 'loginresponse' to be a map" )
65+ }
66+ return loginResponse , nil
67+ }
68+
69+ func getResponseBooleanValue (response map [string ]interface {}, key string ) (bool , bool ) {
70+ v , found := response [key ]
71+ if ! found {
72+ return false , false
73+ }
74+ switch value := v .(type ) {
75+ case bool :
76+ return true , value
77+ case string :
78+ return true , strings .ToLower (value ) == "true"
79+ case float64 :
80+ return true , value != 0
81+ default :
82+ return true , false
83+ }
84+ }
85+
86+ func checkLogin2FAPromptAndValidate (r * Request , response map [string ]interface {}, sessionKey string ) error {
87+ config .Debug ("Checking if 2FA is enabled and verified for the user " , response )
88+ found , is2faEnabled := getResponseBooleanValue (response , "is2faenabled" )
89+ if ! found || ! is2faEnabled {
90+ config .Debug ("2FA is not enabled for the user, skipping 2FA validation" )
91+ return nil
92+ }
93+ found , is2faVerified := getResponseBooleanValue (response , "is2faverified" )
94+ if ! found || is2faVerified {
95+ config .Debug ("2FA is already verified for the user, skipping 2FA validation" )
96+ return nil
97+ }
98+ activeSpinners := r .Config .PauseActiveSpinners ()
99+ fmt .Print ("Enter 2FA code: " )
100+ var code string
101+ fmt .Scanln (& code )
102+ if activeSpinners > 0 {
103+ r .Config .ResumePausedSpinners ()
104+ }
105+ params := make (url.Values )
106+ params .Add ("command" , "validateUserTwoFactorAuthenticationCode" )
107+ params .Add ("codefor2fa" , code )
108+ params .Add ("sessionkey" , sessionKey )
109+
110+ msURL , _ := url .Parse (r .Config .ActiveProfile .URL )
111+
112+ config .Debug ("Validating 2FA with POST URL:" , msURL , params )
113+ spinner := r .Config .StartSpinner ("trying to validate 2FA..." )
114+ resp , err := r .Client ().PostForm (msURL .String (), params )
115+ r .Config .StopSpinner (spinner )
116+ if err != nil {
117+ return errors .New ("failed to failed to validate 2FA code: " + err .Error ())
118+ }
119+ config .Debug ("ValidateUserTwoFactorAuthenticationCode POST response status code:" , resp .StatusCode )
120+ if resp .StatusCode != http .StatusOK {
121+ r .Client ().Jar , _ = cookiejar .New (nil )
122+ return errors .New ("failed to validate 2FA code, please check the code. Invalidating session" )
123+ }
124+ return nil
125+ }
126+
52127// Login logs in a user based on provided request and returns http client and session key
53128func Login (r * Request ) (string , error ) {
54129 params := make (url.Values )
@@ -81,6 +156,13 @@ func Login(r *Request) (string, error) {
81156 return "" , e
82157 }
83158
159+ body , _ := ioutil .ReadAll (resp .Body )
160+ config .Debug ("Login response body:" , string (body ))
161+ loginResponse , err := getLoginResponse (body )
162+ if err != nil {
163+ return "" , err
164+ }
165+
84166 var sessionKey string
85167 curTime := time .Now ()
86168 expiryDuration := 15 * time .Minute
@@ -98,6 +180,9 @@ func Login(r *Request) (string, error) {
98180 }()
99181
100182 config .Debug ("Login sessionkey:" , sessionKey )
183+ if err := checkLogin2FAPromptAndValidate (r , loginResponse , sessionKey ); err != nil {
184+ return "" , err
185+ }
101186 return sessionKey , nil
102187}
103188
0 commit comments