authenticating_user = $authenticating_user; } /** * @param App $app * * @return null|Result_HTML|Result_User|Result_Error */ public function handle( App $app ) { $this->app = $app; $this->request = $app->get_request(); $this->step_token = $app->get_step_token(); $step_token_cookie = $this->step_token->get_token_cookie(); // Not a POST request. if ( ! $this->request->is_post_request() ) { return null; } if ( $this->is_login_or_password_submitted() ) { return $this->handle_login_and_password_login(); } if ( $step_token_cookie ) { return $this->handle_totp_login(); } return $this->handle_no_login_and_no_cookie(); } /** * @return null|Result_Error */ private function handle_no_login_and_no_cookie() { if ( $this->is_totp_token_submitted() ) { return $this->create_invalid_step_token_error_result(); } return null; } /** * @return null|Result_HTML */ private function handle_login_and_password_login() { if ( ! $this->is_submitted_login_valid() || ! $this->should_authenticate_user_using_2fa() ) { return null; } $this->init_totp_login_session(); return $this->render_login_second_step(); } /** * @return Result_Error|Result_HTML|Result_User */ private function handle_totp_login() { $user = $this->get_user_from_step_token_cookie(); $request = $this->app->get_request(); if ( ! $user ) { $this->step_token->delete_token_cookie(); return $this->create_invalid_step_token_error_result(); } try { $this->app->get_totp_login_validator()->validate( $this->app, $user ); } catch ( Validation_Exception $e ) { return $this->render_login_second_step( $e->getMessage() ); } $this->step_token->delete_token_from_user_meta( $user ); $this->step_token->delete_token_cookie(); if ( $request->get_from_post( 'twofas_light_save_device_as_trusted' ) ) { $this->app->get_trusted_device_manager( $user )->create(); } return new Result_User( $user ); } private function init_totp_login_session() { $user = new User( $this->authenticating_user->ID ); $hash = $this->step_token->generate_hash(); $this->step_token->store_token_in_user_meta( $user, $hash ); $this->step_token->store_token_cookie( $user, $hash ); } /** * @param string $error_message * * @return Result_HTML */ private function render_login_second_step( $error_message = '' ) { $app = $this->app; $request = $this->app->get_request(); $html = $app->get_view_renderer()->render( 'login_second_step.html.twig', array( 'wp_login_url' => wp_login_url(), 'twofas_light_login_error' => $this->wrap_error_message_with_wp_error( $error_message ), 'rememberme' => $request->get_from_post( 'rememberme' ), 'redirect_to' => $request->get_from_post( 'redirect_to' ), 'testcookie' => $request->get_from_post( 'testcookie' ), 'reauth' => $request->get_from_post( 'reauth' ), 'twofas_light_save_device_as_trusted' => $request->get_from_post( 'twofas_light_save_device_as_trusted' ), 'interim_login' => $request->get_from_post( 'interim-login' ) ) ); return new Result_HTML( $html ); } /** * @return User|null */ private function get_user_from_step_token_cookie() { $token_cookie = $this->step_token->get_token_cookie(); if ( ! $token_cookie || ! $token_cookie->get_user_login() ) { return null; } $user = User::get_user_by_login( $token_cookie->get_user_login() ); if ( ! $user ) { return null; } return $this->step_token->validate_user_token( $user ) ? $user : null; } /** * @return bool */ public function is_login_or_password_submitted() { if ( is_a( $this->authenticating_user, '\WP_User' ) ) { return true; } $error_codes = $this->authenticating_user->get_error_codes(); $expected_error_codes = array( 'empty_username', 'empty_password' ); $matched_error_codes = array_intersect( $error_codes, $expected_error_codes ); return count( $matched_error_codes ) < 2; } /** * @return bool */ public function is_submitted_login_valid() { return is_a( $this->authenticating_user, '\WP_User' ); } /** * @return bool */ public function should_authenticate_user_using_2fa() { $user = new User( $this->authenticating_user->ID ); $is_device_trusted = $this->app->get_trusted_device_manager( $user )->get_from_cookie() !== null; return $user->is_totp_configured() && $user->is_totp_enabled() && ! $is_device_trusted; } /** * @return bool */ private function is_totp_token_submitted() { return $this->request->get_from_post( 'twofas_light_totp_token' ) !== null; } /** * @return Result_Error */ private function create_invalid_step_token_error_result() { $error_factory = $this->app->get_error_factory(); $wp_error = $error_factory->create_wp_error( 'twofas-invalid-step-token', '2FAS Light session expired or is invalid, please log in again.' ); return new Result_Error( $wp_error ); } /** * @param string $error_message * * @return WP_Error|null */ private function wrap_error_message_with_wp_error( $error_message ) { if ( empty( $error_message ) ) { return null; } $error_factory = $this->app->get_error_factory(); return $error_factory->create_wp_error( 'twofas_light_login_error', $error_message ); } }