tb_login = $tb_login; $this->tb_access_code = $tb_access_code; $this->db = get_db('', ENABLE_DB_DEBUG); } private const ACCESS_CODE_LENGTH = 30; const ONE_TIME_KEY_LENGTH = 15; public function checkOneTimeKey($auth_key) { $db_response = $this->db->select( $this->tb_onetime_key, ['user_id', 'user_name', 'client_id', 'create_time'], [ 'auth_key' => ["=", $auth_key], ], '', 1 ); if($db_response->getCode()) { return null; } $info = $db_response->getData(); if($info) { // used ONCE and delete the key $this->db->runQuery("DELETE FROM `".$this->tb_onetime_key."` WHERE `user_id` = ? ", ['s'], [$info['user_id']]); return $info; } return false; } // auth key allows users to export data (i.e. export to excel) for offline use public function createNewOneTimeKey($user_id) { // and make a new one $auth_key = IDGenerator::createStringId(self::ONE_TIME_KEY_LENGTH); $this->db->insert($this->tb_onetime_key, [ 'user_id' => $user_id, 'auth_key' => $auth_key, 'create_time' => CURRENT_TIME, ]); return $auth_key; } // for all subsequent requests, API need to provide access-code in the request's header // the server will verify the code public function checkAccessCode($access_code) { return $this->db->select( $this->tb_access_code, ['user_id', 'create_time'], [ 'access_code' => ["=", $access_code], ], '', 1 ); } public function deleteAllAccessCode($user_id) { $this->db->runQuery("DELETE FROM `".$this->tb_access_code."` WHERE `user_id` = ? ", ['s'], [$user_id]); } protected function createNewAccessCode($user_id) { // when use login here, delete all other access code $this->deleteAllAccessCode($user_id); // and make a new one $access_code = IDGenerator::createStringId(self::ACCESS_CODE_LENGTH); $user_device = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'; $db_response = $this->db->insert($this->tb_access_code, [ 'user_id' => $user_id, 'access_code' => $access_code, 'user_device' => substr($user_device, 0, 150), 'create_time' => CURRENT_TIME, ]); return $access_code; } /** * allow to login via mobile * - step 1: sms an OTP code to mobile number * - step 2: verify OTP code (mobile number and otp code sent along in the form) */ public function checkLoginViaMobile($mobile, $otp) { // todo } /** * @description An OTP code is sent to user's email. This method helps user need not remember the password * @param $email string * @param $otp string */ public function checkLoginByOTP($user_id, $otp) { $info = $this->db->select( $this->tb_login, [], [ 'user_id' => ["=", $user_id], 'login_otp' => ["=", $otp], ], '', 1 ); if($info) { // return to browser return array( 'user_id' => $user_id, 'access_code' => $this->createNewAccessCode($user_id), ); } return false; } public function createLoginOTP($user_id) { // check email exist $info = $this->db->select( $this->tb_login, [], [ 'user_id' => ["=", $user_id], ], '', 1 ); if($info) { $otp = IDGenerator::createStringId(6); $this->db->update( $this->tb_login, [ 'login_otp' => $otp ], [ 'user_id' => $info['user_id'], ] ); return $otp; } return false; } public function createOrUpdatePassword($user_id, $new_password) { $query = $this->db->runQuery( "SELECT `user_id` FROM `".$this->tb_login."` WHERE `user_id` = ? LIMIT 1 ", ['d'], [ $user_id ] ); if($this->db->fetchAssoc($query)) { return $this->updatePassword($user_id, $new_password); } return $this->createPassword($user_id, $new_password); } protected function createPassword($user_id, $new_password) { return $this->db->insert( $this->tb_login, [ 'user_id' => $user_id, 'password_hash' => $this->hashPassword($new_password), 'create_time' => CURRENT_TIME, 'create_by' => '', ] ); } protected function updatePassword($user_id, $new_password) { return $this->db->update( $this->tb_login, [ 'password_hash' => $this->hashPassword($new_password), 'last_update' => CURRENT_TIME, 'last_update_by' => ADMIN_NAME, ], [ 'user_id' => $user_id, ] ); } /** * @param $user_id int * @param $password string * @return array|false */ public function checkLogin($user_id, $password) { $info = $this->db->select( $this->tb_login, [ 'password_hash'], [ 'user_id' => ["=", $user_id], ], '', 1 ); //test password if($info && $this->verifyHash($password, $info['password_hash'])) { $this->updateUserLogin($user_id); // return to browser return array( 'user_id' => $user_id, 'access_code' => $this->createNewAccessCode($user_id), ); } return false; } private function updateUserLogin($user_id){ return $this->db->update( $this->tb_login, [ 'last_login_time' => CURRENT_TIME, 'last_login_ip' => USER_IP, 'last_login_device' => '', 'last_login_session_id' => \Hura8\System\Security\Session::id() ?: '', 'last_login_browser' => USER_AGENT, ], [ 'user_id' => $user_id, ] ); } /** * @param $str string * @return string */ private function hashPassword($str) { return password_hash($str, PASSWORD_BCRYPT, array('cost' => 12 )); } /** * 15-04-2016 verify string with given hash * @param $str_to_verify string * @param $hash string * @return boolean */ private function verifyHash($str_to_verify, $hash) { return (password_verify($str_to_verify, $hash)); } }