From fa032ee96fa49fd4d7c12ef110f1e3546d281b2d Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 15 Oct 2018 22:11:35 +0200 Subject: [PATCH 1/4] Add some explanation for activation_code --- libraries/Ion_auth.php | 2 +- models/Ion_auth_model.php | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/libraries/Ion_auth.php b/libraries/Ion_auth.php index c0635234b..345357bd0 100644 --- a/libraries/Ion_auth.php +++ b/libraries/Ion_auth.php @@ -264,7 +264,7 @@ public function register($identity, $password, $email, $additional_data = [], $g return FALSE; } - // deactivate so the user much follow the activation flow + // deactivate so the user must follow the activation flow $deactivate = $this->ion_auth_model->deactivate($id); // the deactivate method call adds a message, here we need to clear that diff --git a/models/Ion_auth_model.php b/models/Ion_auth_model.php index 867fb0fc1..7793d5fc8 100644 --- a/models/Ion_auth_model.php +++ b/models/Ion_auth_model.php @@ -46,7 +46,27 @@ class Ion_auth_model extends CI_Model /** * activation code - * + * + * Set by deactivate() function + * Also set on register() function, if email_activation + * option is activated + * + * This is the value devs should give to the user + * (in an email, usually) + * + * It contains the *user* version of the activation code + * It's a value of the form "selector.validator" + * + * This is not the same activation_code as the one in DB. + * The DB contains a *hashed* version of the validator + * and a selector in another column. + * + * THe selector is not private, and only used to lookup + * the validator. + * + * The validator is private, and to be only known by the user + * So in case of DB leak, nothing could be actually used. + * * @var string */ public $activation_code; @@ -2644,6 +2664,7 @@ protected function _get_hash_algo() /** * Generate a random selector/validator couple + * This is a user code * * @param $selector_size int size of the selector token * @param $validator_size int size of the validator token @@ -2677,7 +2698,7 @@ protected function _generate_selector_validator_couple($selector_size = 40, $val /** * Retrieve remember cookie info * - * @param $user_code string + * @param $user_code string A user code of the form "selector.validator" * * @return object * ->selector simple token to retrieve the user in DB From f196efbb2d595c1e7a93bf06b5817b33a721daf8 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 15 Oct 2018 22:11:53 +0200 Subject: [PATCH 2/4] Remove forgotten_password_code member no longer used --- models/Ion_auth_model.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/models/Ion_auth_model.php b/models/Ion_auth_model.php index 7793d5fc8..dccfc805f 100644 --- a/models/Ion_auth_model.php +++ b/models/Ion_auth_model.php @@ -71,13 +71,6 @@ class Ion_auth_model extends CI_Model */ public $activation_code; - /** - * forgotten password key - * - * @var string - */ - public $forgotten_password_code; - /** * new password * From 84968421db519f8653ec3644f5dade14df2609a0 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 15 Oct 2018 22:12:21 +0200 Subject: [PATCH 3/4] Split activate into two functions (check and activate) --- models/Ion_auth_model.php | 67 ++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/models/Ion_auth_model.php b/models/Ion_auth_model.php index dccfc805f..01de2c30b 100644 --- a/models/Ion_auth_model.php +++ b/models/Ion_auth_model.php @@ -373,52 +373,55 @@ public function rehash_password_if_needed($hash, $identity, $password) } /** - * Validates and removes activation code. + * Check a user activation code * - * @param int|string $id - * @param bool $code if omitted, simply activate the user without check + * @param int|string $id the user identifier + * @param bool $code the activation code + * It's the *user* one, containing "selector.validator" + * the one you got in activation_code member + * if omitted, simply activate the user without check * * @return bool * @author Mathew */ - public function activate($id, $code = FALSE) + public function check_user_code_activation($id, $code) { - $this->trigger_events('pre_activate'); - $token = $this->_retrieve_selector_validator_couple($code); + + $query = $this->db->select([$this->identity_column, 'activation_code']) + ->where('activation_selector', $token->selector) + ->where('id', $id) + ->limit(1) + ->get($this->tables['users']); - if ($token !== FALSE) + if ($query->num_rows() === 1) { - // A token was provided, we need to check it - - $query = $this->db->select([$this->identity_column, 'activation_code']) - ->where('activation_selector', $token->selector) - ->where('id', $id) - ->limit(1) - ->get($this->tables['users']); + $user = $query->row(); - if ($query->num_rows() === 1) + if ($this->verify_password($token->validator, $user->activation_code)) { - $user = $query->row(); - - if ($this->verify_password($token->validator, $user->activation_code)) - { - $data = [ - 'activation_selector' => NULL, - 'activation_code' => NULL, - 'active' => 1 - ]; - - $this->trigger_events('extra_where'); - $this->db->update($this->tables['users'], $data, ['id' => $id]); - return TRUE; - } + return TRUE; } } - else - { - // A token was NOT provided, simply activate the user + return FALSE; + } + + /** + * Validates and removes activation code. + * + * @param int|string $id the user identifier + * @param bool $code the *user* activation code + * + * @return bool + * @author Mathew + */ + public function activate($id, $code = FALSE) + { + $this->trigger_events('pre_activate'); + + if ($code === FALSE || $this->check_user_code_activation($id, $code) === TRUE) + { $data = [ 'activation_selector' => NULL, 'activation_code' => NULL, From c96e910f27ea3e03c3881590f43679a8982819ee Mon Sep 17 00:00:00 2001 From: Guillaume Date: Tue, 16 Oct 2018 21:49:50 +0200 Subject: [PATCH 4/4] Define get_user_by_activation_code() --- models/Ion_auth_model.php | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/models/Ion_auth_model.php b/models/Ion_auth_model.php index 01de2c30b..f6a942339 100644 --- a/models/Ion_auth_model.php +++ b/models/Ion_auth_model.php @@ -373,34 +373,29 @@ public function rehash_password_if_needed($hash, $identity, $password) } /** - * Check a user activation code + * Get a user by its activation code * - * @param int|string $id the user identifier - * @param bool $code the activation code + * @param bool $user_code the activation code * It's the *user* one, containing "selector.validator" * the one you got in activation_code member - * if omitted, simply activate the user without check * - * @return bool - * @author Mathew + * @return bool|object + * @author Indigo */ - public function check_user_code_activation($id, $code) + public function get_user_by_activation_code($user_code) { - $token = $this->_retrieve_selector_validator_couple($code); + // Retrieve the token object from the code + $token = $this->_retrieve_selector_validator_couple($user_code); - $query = $this->db->select([$this->identity_column, 'activation_code']) - ->where('activation_selector', $token->selector) - ->where('id', $id) - ->limit(1) - ->get($this->tables['users']); + // Retrieve the user according to this selector + $user = $this->where('activation_selector', $token->selector)->users()->row(); - if ($query->num_rows() === 1) + if ($user) { - $user = $query->row(); - + // Check the hash against the validator if ($this->verify_password($token->validator, $user->activation_code)) { - return TRUE; + return $user; } } @@ -412,6 +407,7 @@ public function check_user_code_activation($id, $code) * * @param int|string $id the user identifier * @param bool $code the *user* activation code + * if omitted, simply activate the user without check * * @return bool * @author Mathew @@ -420,7 +416,13 @@ public function activate($id, $code = FALSE) { $this->trigger_events('pre_activate'); - if ($code === FALSE || $this->check_user_code_activation($id, $code) === TRUE) + if ($code !== FALSE) { + $user = $this->get_user_by_activation_code($code); + } + + // Activate if no code is given + // Or if a user was found with this code, and that it matches the id + if ($code === FALSE || ($user && $user->id === $id)) { $data = [ 'activation_selector' => NULL,