@@ -49,6 +49,84 @@ 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+ if ! r .Config .HasShell {
88+ return nil
89+ }
90+ config .Debug ("Checking if 2FA is enabled and verified for the user " , response )
91+ found , is2faEnabled := getResponseBooleanValue (response , "is2faenabled" )
92+ if ! found || ! is2faEnabled {
93+ config .Debug ("2FA is not enabled for the user, skipping 2FA validation" )
94+ return nil
95+ }
96+ found , is2faVerified := getResponseBooleanValue (response , "is2faverified" )
97+ if ! found || is2faVerified {
98+ config .Debug ("2FA is already verified for the user, skipping 2FA validation" )
99+ return nil
100+ }
101+ activeSpinners := r .Config .PauseActiveSpinners ()
102+ fmt .Print ("Enter 2FA code: " )
103+ var code string
104+ fmt .Scanln (& code )
105+ if activeSpinners > 0 {
106+ r .Config .ResumePausedSpinners ()
107+ }
108+ params := make (url.Values )
109+ params .Add ("command" , "validateUserTwoFactorAuthenticationCode" )
110+ params .Add ("codefor2fa" , code )
111+ params .Add ("sessionkey" , sessionKey )
112+
113+ msURL , _ := url .Parse (r .Config .ActiveProfile .URL )
114+
115+ config .Debug ("Validating 2FA with POST URL:" , msURL , params )
116+ spinner := r .Config .StartSpinner ("trying to validate 2FA..." )
117+ resp , err := r .Client ().PostForm (msURL .String (), params )
118+ r .Config .StopSpinner (spinner )
119+ if err != nil {
120+ return errors .New ("failed to failed to validate 2FA code: " + err .Error ())
121+ }
122+ config .Debug ("ValidateUserTwoFactorAuthenticationCode POST response status code:" , resp .StatusCode )
123+ if resp .StatusCode != http .StatusOK {
124+ r .Client ().Jar , _ = cookiejar .New (nil )
125+ return errors .New ("failed to validate 2FA code, please check the code. Invalidating session" )
126+ }
127+ return nil
128+ }
129+
52130// Login logs in a user based on provided request and returns http client and session key
53131func Login (r * Request ) (string , error ) {
54132 params := make (url.Values )
@@ -81,6 +159,13 @@ func Login(r *Request) (string, error) {
81159 return "" , e
82160 }
83161
162+ body , _ := ioutil .ReadAll (resp .Body )
163+ config .Debug ("Login response body:" , string (body ))
164+ loginResponse , err := getLoginResponse (body )
165+ if err != nil {
166+ return "" , err
167+ }
168+
84169 var sessionKey string
85170 curTime := time .Now ()
86171 expiryDuration := 15 * time .Minute
@@ -98,6 +183,9 @@ func Login(r *Request) (string, error) {
98183 }()
99184
100185 config .Debug ("Login sessionkey:" , sessionKey )
186+ if err := checkLogin2FAPromptAndValidate (r , loginResponse , sessionKey ); err != nil {
187+ return "" , err
188+ }
101189 return sessionKey , nil
102190}
103191
0 commit comments