├── config ├── autoload.php └── auth.php ├── database.sql ├── demo ├── database.sql ├── controllers │ ├── logout.php │ ├── install.php │ ├── admin.php │ └── login.php ├── views │ └── login.php ├── models │ └── user_model.php └── libraries │ └── PasswordHash.php ├── spark.info ├── models └── autologin_model.php ├── README.md └── libraries └── Auth.php /config/autoload.php: -------------------------------------------------------------------------------- 1 | load->library('auth'); 10 | 11 | $this->auth->logout(); 12 | redirect('login'); 13 | } 14 | 15 | } 16 | 17 | /* End of file logout.php */ 18 | /* Location: ./application/controllers/logout.php */ -------------------------------------------------------------------------------- /demo/views/login.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/controllers/install.php: -------------------------------------------------------------------------------- 1 | load->model('user_model'); 14 | 15 | /* EDIT THESE FIELDS */ 16 | $user = array(); 17 | $user['username'] = 'admin'; 18 | $user['password'] = 'pass'; 19 | $user['email'] = 'my@mail.com'; 20 | 21 | $id = $this->user_model->insert($user); 22 | } 23 | } 24 | 25 | /* End of file install.php */ 26 | /* Location: ./application/controllers/install.php */ -------------------------------------------------------------------------------- /demo/controllers/admin.php: -------------------------------------------------------------------------------- 1 | load->library('auth'); 10 | 11 | if (!$this->auth->loggedin()) { 12 | redirect('login'); 13 | } 14 | 15 | // get current user id 16 | $id = $this->auth->userid(); 17 | 18 | // get user from database 19 | $this->load->model('user_model'); 20 | $user = $this->user_model->get('id', $id); 21 | 22 | echo 'Welcome to the super secret section, ' . $user['username']; 23 | } 24 | 25 | } 26 | 27 | /* End of file admin.php */ 28 | /* Location: ./application/controllers/admin.php */ -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | load->library('auth'); 10 | 11 | // user is already logged in 12 | if ($this->auth->loggedin()) { 13 | redirect('admin'); 14 | } 15 | 16 | $error = ''; 17 | 18 | // form submitted 19 | if ($this->input->post('username') && $this->input->post('password')) { 20 | $remember = $this->input->post('remember') ? TRUE : FALSE; 21 | 22 | // get user from database 23 | $this->load->model('user_model'); 24 | $user = $this->user_model->get('username', $this->input->post('username')); 25 | 26 | if ($user) { 27 | // compare passwords 28 | if ($this->user_model->check_password($this->input->post('password'), $user['password'])) { 29 | // mark user as logged in 30 | $this->auth->login($user['id'], $remember); 31 | redirect('admin'); 32 | } else { 33 | $error = 'Wrong password'; 34 | } 35 | } else { 36 | $error = 'User does not exist'; 37 | } 38 | } 39 | 40 | // show login form 41 | $this->load->helper('form'); 42 | $this->load->view('login', array('error' => $error)); 43 | } 44 | } 45 | 46 | /* End of file login.php */ 47 | /* Location: ./application/controllers/login.php */ -------------------------------------------------------------------------------- /models/autologin_model.php: -------------------------------------------------------------------------------- 1 | config->load('auth'); 19 | $this->table = $this->config->item('autologin_table'); 20 | $this->expire = $this->config->item('autologin_expire'); 21 | } 22 | 23 | /** 24 | * Get the private key for a specific user and series 25 | */ 26 | public function get($user, $series) { 27 | $this->db->where('user', $user); 28 | $this->db->where('series', $series); 29 | $row = $this->db->get($this->table)->row(); 30 | 31 | return $row ? $row->key : FALSE; 32 | } 33 | 34 | /** 35 | * Extend a user's current series with a new key 36 | */ 37 | public function update($user, $series, $private) { 38 | $this->db->where('user', $user); 39 | $this->db->where('series', $series); 40 | 41 | return $this->db->update($this->table, array('key' => $private, 'created' => time())); 42 | } 43 | 44 | /** 45 | * Start a new serie for a user 46 | */ 47 | public function insert($user, $series, $private) { 48 | return $this->db->insert($this->table, array('user' => $user, 'series' => $series, 'key' => $private, 'created' => time())); 49 | } 50 | 51 | /** 52 | * Dlete a user's series 53 | */ 54 | public function delete($user, $series) { 55 | $this->db->where('user', $user); 56 | $this->db->where('series', $series); 57 | 58 | return $this->db->delete($this->table); 59 | } 60 | 61 | /** 62 | * Remove all expired keys 63 | */ 64 | public function purge() { 65 | $this->db->where('created <', time() - $this->expire)->delete($this->table); 66 | } 67 | } -------------------------------------------------------------------------------- /demo/models/user_model.php: -------------------------------------------------------------------------------- 1 | load->library("auth"); 22 | 23 | $user['password'] = $this->hash($user['password']); 24 | $user['registered'] = time(); 25 | 26 | $this->db->insert($this->table, $user); 27 | return $this->db->insert_id(); 28 | } 29 | 30 | /** 31 | * Update a user, password will be hashed 32 | * 33 | * @param int id 34 | * @param array user 35 | * @return int id 36 | */ 37 | public function update($id, $user) { 38 | // prevent overwriting with a blank password 39 | if (isset($user['password']) && $user['password']) { 40 | $user['password'] = $this->hash($user['password']); 41 | } else { 42 | unset($user['password']); 43 | } 44 | 45 | $this->db->where('id', $id)->update($this->table, $user); 46 | return $id; 47 | } 48 | 49 | /** 50 | * Delete a user 51 | * 52 | * @param string where 53 | * @param int value 54 | * @param string identification field 55 | */ 56 | public function delete($where, $value = FALSE) { 57 | if (!$value) { 58 | $value = $where; 59 | $where = 'id'; 60 | } 61 | 62 | $this->db->where($where, $value)->delete($this->table); 63 | } 64 | 65 | /** 66 | * Retrieve a user 67 | * 68 | * @param string where 69 | * @param int value 70 | * @param string identification field 71 | */ 72 | public function get($where, $value = FALSE) { 73 | if (!$value) { 74 | $value = $where; 75 | $where = 'id'; 76 | } 77 | 78 | $user = $this->db->where($where, $value)->get($this->table)->row_array(); 79 | return $user; 80 | } 81 | 82 | /** 83 | * Get a list of users with pagination options 84 | * 85 | * @param int limit 86 | * @param int offset 87 | * @return array users 88 | */ 89 | public function get_list($limit = FALSE, $offset = FALSE) { 90 | if ($limit) { 91 | return $this->db->order_by("username")->limit($limit, $offset)->get($this->table)->result_array(); 92 | } else { 93 | return $this->db->order_by("username")->get($this->table)->result_array(); 94 | } 95 | } 96 | 97 | /** 98 | * Check if a user exists 99 | * 100 | * @param string where 101 | * @param int value 102 | * @param string identification field 103 | */ 104 | 105 | public function exists($where, $value = FALSE) { 106 | if (!$value) { 107 | $value = $where; 108 | $where = 'id'; 109 | } 110 | 111 | return $this->db->where($where, $value)->count_all_results($this->table); 112 | } 113 | 114 | /** 115 | * Password hashing function 116 | * 117 | * @param string $password 118 | */ 119 | public function hash($password) { 120 | $this->load->library('PasswordHash', array('iteration_count_log2' => 8, 'portable_hashes' => FALSE)); 121 | 122 | // hash password 123 | return $this->passwordhash->HashPassword($password); 124 | } 125 | 126 | /** 127 | * Compare user input password to stored hash 128 | * 129 | * @param string $password 130 | * @param string $stored_hash 131 | */ 132 | public function check_password($password, $stored_hash) { 133 | $this->load->library('PasswordHash', array('iteration_count_log2' => 8, 'portable_hashes' => FALSE)); 134 | 135 | // check password 136 | return $this->passwordhash->CheckPassword($password, $stored_hash); 137 | } 138 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CodeIgniter Secure Authentication Library 2 | ========================================= 3 | 4 | This is a secure authentication library for codeigniter. 5 | 6 | **WARNING**: this is version 2 of this library, a more simplified, easier to use version that is easier to implement in existing code. The original library relied too much on correct model communication that now has been removed. Most of the functionality has been preserved, although some things have been moved to the model as you can see in the example folder. 7 | 8 | Installation 9 | ------------ 10 | 11 | Place the files from the repository in their respective folders (or use spark). A database.sql file is included containing the required database structure. 12 | 13 | Configuration 14 | ------------- 15 | 16 | Edit the auth.php configuration file to fit your specific environment: 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Authentication configuration 21 | |-------------------------------------------------------------------------- 22 | | The basic settings for the auth library. 23 | | 24 | | 'cookie_name' = the name you want for the cookie 25 | | 'cookie_encrypt' = encrypt cookie with encryption_key 26 | | 'autologin_expire' = time for cookie to expire in seconds (renews when used) 27 | | 'autologin_table' = the name of the autologin table (see .sql file) 28 | | 'hash_algorithm' = the hashing algorithm used for generating keys 29 | */ 30 | 31 | $config['cookie_name'] = 'autologin'; 32 | $config['cookie_encrypt'] = TRUE; 33 | $config['autologin_table'] = 'autologin'; 34 | $config['autologin_expire'] = 5184000; // 60 days 35 | $config['hash_algorithm'] = 'sha256'; 36 | 37 | If you prefer, you can autoload the library by adjusting your autoload.php file and add 'auth' to the $autoload['libraries'] array. 38 | 39 | Usage 40 | ----- 41 | 42 | A simple implementation example of this library is included, so be sure to check out the demo folder. These are the available methods: 43 | 44 | $this->auth->login($id, $remember = TRUE) 45 | Mark the user with this id as logged in, provide an optional remember boolean if you want to create an autologin cookie 46 | 47 | $this->auth->logout() 48 | Logout function, this removes the autologin cookie and the active key 49 | 50 | $this->auth->loggedin() 51 | Returns whether the user is logged in or not, TRUE/FALSE 52 | 53 | $this->auth->userid() 54 | Returns the current user's id 55 | 56 | Details & Security 57 | ------------------ 58 | 59 | This library was inspired by the following articles: 60 | 61 | - http://www.shinytype.com/php/persistent-login-protocol/ 62 | - http://jaspan.com/improved_persistent_login_cookie_best_practice 63 | 64 | When a user logs in with 'remember me' checked, a login cookie is created containing the user's identification and a personal key. Actually 2 keys are created, one for the user's cookie and one to store into the database. A user can only log in if both key pairs are present. 65 | 66 | When that user visits the site again, it presents the login cookie. The database version of the key is compared with the key stored in the cookie. If the relation between both keys is correct, the user is logged in, the used key pair will be removed and a new key pair is generated for future use. 67 | 68 | If on the other hand, the key pair is invalid, a possible cookie/key theft assumed. The user's active key will then immediately be removed for safety reasons. 69 | 70 | Controller example 71 | ------------------ 72 | 73 | In the demo folder you can find a fully working example of this library. It also includes a basic user model and an extra .sql script to create the users database table. 74 | 75 | Here is an example how you _could_ use the library on your login page: 76 | 77 | // form submitted 78 | if ($this->input->post('username') && $this->input->post('password')) { 79 | $remember = $this->input->post('remember') ? TRUE : FALSE; 80 | 81 | // get user from database 82 | $this->load->model('user_model'); 83 | $user = $this->user_model->get('username', $this->input->post('username')); 84 | 85 | if ($user) { 86 | // compare passwords 87 | if ($this->user_model->check_password($this->input->post('password'), $user['password'])) { 88 | // mark user as logged in 89 | $this->auth->login($user['id'], $remember); 90 | redirect('admin'); 91 | } else { 92 | $error = "Wrong password"; 93 | } 94 | } else { 95 | $error = "User does not exist"; 96 | } 97 | } -------------------------------------------------------------------------------- /demo/libraries/PasswordHash.php: -------------------------------------------------------------------------------- 1 | in 2004-2006 and placed in 8 | # the public domain. Revised in subsequent years, still public domain. 9 | # 10 | # There's absolutely no warranty. 11 | # 12 | # The homepage URL for this framework is: 13 | # 14 | # http://www.openwall.com/phpass/ 15 | # 16 | # Please be sure to update the Version line if you edit this file in any way. 17 | # It is suggested that you leave the main version number intact, but indicate 18 | # your project name (after the slash) and add your own revision information. 19 | # 20 | # Please do not change the "private" password hashing method implemented in 21 | # here, thereby making your hashes incompatible. However, if you must, please 22 | # change the hash type identifier (the "$P$") to something different. 23 | # 24 | # Obviously, since this code is in the public domain, the above are not 25 | # requirements (there can be none), but merely suggestions. 26 | # 27 | class PasswordHash { 28 | var $itoa64; 29 | var $iteration_count_log2; 30 | var $portable_hashes; 31 | var $random_state; 32 | 33 | function PasswordHash($params = array()) 34 | { 35 | $iteration_count_log2 = (isset($params['iteration_count_log2'])) ? $params['iteration_count_log2'] : 8; 36 | $portable_hashes = (isset($params['portable_hashes'])) ? $params['portable_hashes'] : FALSE; 37 | 38 | $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 39 | 40 | if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) 41 | $iteration_count_log2 = 8; 42 | $this->iteration_count_log2 = $iteration_count_log2; 43 | 44 | $this->portable_hashes = $portable_hashes; 45 | 46 | $this->random_state = microtime(); 47 | if (function_exists('getmypid')) 48 | $this->random_state .= getmypid(); 49 | } 50 | 51 | function get_random_bytes($count) 52 | { 53 | $output = ''; 54 | if (is_readable('/dev/urandom') && 55 | ($fh = @fopen('/dev/urandom', 'rb'))) { 56 | $output = fread($fh, $count); 57 | fclose($fh); 58 | } 59 | 60 | if (strlen($output) < $count) { 61 | $output = ''; 62 | for ($i = 0; $i < $count; $i += 16) { 63 | $this->random_state = 64 | md5(microtime() . $this->random_state); 65 | $output .= 66 | pack('H*', md5($this->random_state)); 67 | } 68 | $output = substr($output, 0, $count); 69 | } 70 | 71 | return $output; 72 | } 73 | 74 | function encode64($input, $count) 75 | { 76 | $output = ''; 77 | $i = 0; 78 | do { 79 | $value = ord($input[$i++]); 80 | $output .= $this->itoa64[$value & 0x3f]; 81 | if ($i < $count) 82 | $value |= ord($input[$i]) << 8; 83 | $output .= $this->itoa64[($value >> 6) & 0x3f]; 84 | if ($i++ >= $count) 85 | break; 86 | if ($i < $count) 87 | $value |= ord($input[$i]) << 16; 88 | $output .= $this->itoa64[($value >> 12) & 0x3f]; 89 | if ($i++ >= $count) 90 | break; 91 | $output .= $this->itoa64[($value >> 18) & 0x3f]; 92 | } while ($i < $count); 93 | 94 | return $output; 95 | } 96 | 97 | function gensalt_private($input) 98 | { 99 | $output = '$P$'; 100 | $output .= $this->itoa64[min($this->iteration_count_log2 + 101 | ((PHP_VERSION >= '5') ? 5 : 3), 30)]; 102 | $output .= $this->encode64($input, 6); 103 | 104 | return $output; 105 | } 106 | 107 | function crypt_private($password, $setting) 108 | { 109 | $output = '*0'; 110 | if (substr($setting, 0, 2) == $output) 111 | $output = '*1'; 112 | 113 | $id = substr($setting, 0, 3); 114 | # We use "$P$", phpBB3 uses "$H$" for the same thing 115 | if ($id != '$P$' && $id != '$H$') 116 | return $output; 117 | 118 | $count_log2 = strpos($this->itoa64, $setting[3]); 119 | if ($count_log2 < 7 || $count_log2 > 30) 120 | return $output; 121 | 122 | $count = 1 << $count_log2; 123 | 124 | $salt = substr($setting, 4, 8); 125 | if (strlen($salt) != 8) 126 | return $output; 127 | 128 | # We're kind of forced to use MD5 here since it's the only 129 | # cryptographic primitive available in all versions of PHP 130 | # currently in use. To implement our own low-level crypto 131 | # in PHP would result in much worse performance and 132 | # consequently in lower iteration counts and hashes that are 133 | # quicker to crack (by non-PHP code). 134 | if (PHP_VERSION >= '5') { 135 | $hash = md5($salt . $password, TRUE); 136 | do { 137 | $hash = md5($hash . $password, TRUE); 138 | } while (--$count); 139 | } else { 140 | $hash = pack('H*', md5($salt . $password)); 141 | do { 142 | $hash = pack('H*', md5($hash . $password)); 143 | } while (--$count); 144 | } 145 | 146 | $output = substr($setting, 0, 12); 147 | $output .= $this->encode64($hash, 16); 148 | 149 | return $output; 150 | } 151 | 152 | function gensalt_extended($input) 153 | { 154 | $count_log2 = min($this->iteration_count_log2 + 8, 24); 155 | # This should be odd to not reveal weak DES keys, and the 156 | # maximum valid value is (2**24 - 1) which is odd anyway. 157 | $count = (1 << $count_log2) - 1; 158 | 159 | $output = '_'; 160 | $output .= $this->itoa64[$count & 0x3f]; 161 | $output .= $this->itoa64[($count >> 6) & 0x3f]; 162 | $output .= $this->itoa64[($count >> 12) & 0x3f]; 163 | $output .= $this->itoa64[($count >> 18) & 0x3f]; 164 | 165 | $output .= $this->encode64($input, 3); 166 | 167 | return $output; 168 | } 169 | 170 | function gensalt_blowfish($input) 171 | { 172 | # This one needs to use a different order of characters and a 173 | # different encoding scheme from the one in encode64() above. 174 | # We care because the last character in our encoded string will 175 | # only represent 2 bits. While two known implementations of 176 | # bcrypt will happily accept and correct a salt string which 177 | # has the 4 unused bits set to non-zero, we do not want to take 178 | # chances and we also do not want to waste an additional byte 179 | # of entropy. 180 | $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 181 | 182 | $output = '$2a$'; 183 | $output .= chr(ord('0') + $this->iteration_count_log2 / 10); 184 | $output .= chr(ord('0') + $this->iteration_count_log2 % 10); 185 | $output .= '$'; 186 | 187 | $i = 0; 188 | do { 189 | $c1 = ord($input[$i++]); 190 | $output .= $itoa64[$c1 >> 2]; 191 | $c1 = ($c1 & 0x03) << 4; 192 | if ($i >= 16) { 193 | $output .= $itoa64[$c1]; 194 | break; 195 | } 196 | 197 | $c2 = ord($input[$i++]); 198 | $c1 |= $c2 >> 4; 199 | $output .= $itoa64[$c1]; 200 | $c1 = ($c2 & 0x0f) << 2; 201 | 202 | $c2 = ord($input[$i++]); 203 | $c1 |= $c2 >> 6; 204 | $output .= $itoa64[$c1]; 205 | $output .= $itoa64[$c2 & 0x3f]; 206 | } while (1); 207 | 208 | return $output; 209 | } 210 | 211 | function HashPassword($password) 212 | { 213 | $random = ''; 214 | 215 | if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { 216 | $random = $this->get_random_bytes(16); 217 | $hash = 218 | crypt($password, $this->gensalt_blowfish($random)); 219 | if (strlen($hash) == 60) 220 | return $hash; 221 | } 222 | 223 | if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { 224 | if (strlen($random) < 3) 225 | $random = $this->get_random_bytes(3); 226 | $hash = 227 | crypt($password, $this->gensalt_extended($random)); 228 | if (strlen($hash) == 20) 229 | return $hash; 230 | } 231 | 232 | if (strlen($random) < 6) 233 | $random = $this->get_random_bytes(6); 234 | $hash = 235 | $this->crypt_private($password, 236 | $this->gensalt_private($random)); 237 | if (strlen($hash) == 34) 238 | return $hash; 239 | 240 | # Returning '*' on error is safe here, but would _not_ be safe 241 | # in a crypt(3)-like function used _both_ for generating new 242 | # hashes and for validating passwords against existing hashes. 243 | return '*'; 244 | } 245 | 246 | function CheckPassword($password, $stored_hash) 247 | { 248 | $hash = $this->crypt_private($password, $stored_hash); 249 | if ($hash[0] == '*') 250 | $hash = crypt($password, $stored_hash); 251 | 252 | return $hash == $stored_hash; 253 | } 254 | } 255 | 256 | ?> 257 | -------------------------------------------------------------------------------- /libraries/Auth.php: -------------------------------------------------------------------------------- 1 | ci = &get_instance(); 46 | 47 | // load session library 48 | $this->ci->load->library('session'); 49 | 50 | // initialize from config 51 | if (!empty($config)) { 52 | $this->initialize($config); 53 | } 54 | 55 | log_message('debug', 'Authentication library initialized'); 56 | 57 | // detect autologin 58 | if (!$this->ci->session->userdata('auth_loggedin')) { 59 | $this->autologin(); 60 | } 61 | } 62 | 63 | /** 64 | * Initialize with configuration array 65 | * 66 | * @param array $config 67 | */ 68 | public function initialize($config = array()) { 69 | foreach ($config as $key => $val) { 70 | $this->$key = $val; 71 | } 72 | } 73 | 74 | /** 75 | * Mark a user as logged in and create autologin cookie if wanted 76 | * 77 | * @param string $id 78 | * @param boolean $remember 79 | * @return boolean 80 | */ 81 | public function login($id, $remember = TRUE) { 82 | if(!$this->loggedin()) { 83 | // mark user as logged in 84 | $this->ci->session->set_userdata(array('auth_user' => $id, 'auth_loggedin' => TRUE)); 85 | 86 | if ($remember) { 87 | $this->create_autologin($id); 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * Logout the current user, destroys the current session and autologin key 94 | */ 95 | public function logout() { 96 | // mark user as logged out 97 | $this->ci->session->set_userdata(array('auth_user' => FALSE, 'auth_loggedin' => FALSE)); 98 | 99 | // remove cookie and active key 100 | $this->delete_autologin(); 101 | } 102 | 103 | /** 104 | * Check if the current user is logged in or not 105 | * 106 | * @return boolean 107 | */ 108 | public function loggedin() { 109 | return $this->ci->session->userdata('auth_loggedin'); 110 | } 111 | 112 | /** 113 | * Returns the user id of the current user when logged in 114 | * 115 | * @return int 116 | */ 117 | public function userid() { 118 | return $this->loggedin() ? $this->ci->session->userdata('auth_user') : FALSE; 119 | } 120 | 121 | /** 122 | * Generate a new key pair and create the autologin cookie 123 | * 124 | * @param int $id 125 | * @param string $series 126 | */ 127 | private function create_autologin($id, $series = FALSE) { 128 | // generate keys 129 | list($public, $private) = $this->generate_keys(); 130 | 131 | $this->ci->load->model('autologin_model'); 132 | 133 | // create new series or expand current series 134 | if (!$series) { 135 | list($series) = $this->generate_keys(); 136 | $this->ci->autologin_model->insert($id, $series, $private); 137 | } else { 138 | $this->ci->autologin_model->update($id, $series, $private); 139 | } 140 | 141 | // write public key to cookie 142 | $cookie = array('id' => $id, 'series' => $series, 'key' => $public); 143 | $this->write_cookie($cookie); 144 | } 145 | 146 | /** 147 | * Disable the current autologin key and remove the cookie 148 | */ 149 | private function delete_autologin() { 150 | if ($cookie = $this->read_cookie()) { 151 | // remove current series 152 | $this->ci->load->model('autologin_model'); 153 | $this->ci->autologin_model->delete($cookie['id'], $cookie['series']); 154 | 155 | // delete cookie 156 | $this->ci->input->set_cookie(array('name' => $this->cookie_name, 'value' => '', 'expire' => '')); 157 | } 158 | } 159 | 160 | /** 161 | * Detects the autologin cookie and check public/private key pair 162 | * 163 | * @return boolean 164 | */ 165 | private function autologin() { 166 | if ($cookie = $this->read_cookie()) { 167 | // remove expired keys 168 | $this->ci->load->model('autologin_model'); 169 | $this->ci->autologin_model->purge(); 170 | 171 | // get private key 172 | $private = $this->ci->autologin_model->get($cookie['id'], $cookie['series']); 173 | 174 | if ($this->validate_keys($cookie['key'], $private)) { 175 | // mark user as logged in 176 | $this->ci->session->set_userdata(array('auth_user' => $cookie['id'], 'auth_loggedin' => TRUE)); 177 | 178 | // user has a valid key, extend current series with new key 179 | $this->create_autologin($cookie['id'], $cookie['series']); 180 | return TRUE; 181 | } else { 182 | // the key was not valid, strange stuff going on 183 | // remove the active session to prevent theft! 184 | $this->delete_autologin(); 185 | } 186 | } 187 | 188 | return FALSE; 189 | } 190 | 191 | /** 192 | * Write data to autologin cookie 193 | * 194 | * @param array $data 195 | */ 196 | private function write_cookie($data = array()) { 197 | $data = serialize($data); 198 | 199 | // encrypt cookie 200 | if ($this->cookie_encrypt) { 201 | $this->ci->load->library('encrypt'); 202 | $data = $this->ci->encrypt->encode($data); 203 | } 204 | 205 | return $this->ci->input->set_cookie(array('name' => $this->cookie_name, 'value' => $data, 'expire' => $this->autologin_expire)); 206 | } 207 | 208 | /** 209 | * Read data from autologin cookie 210 | * 211 | * @return boolean 212 | */ 213 | private function read_cookie() { 214 | $cookie = $this->ci->input->cookie($this->cookie_name, TRUE); 215 | 216 | if (!$cookie) { 217 | return FALSE; 218 | } 219 | 220 | // decrypt cookie 221 | if ($this->cookie_encrypt) { 222 | $this->ci->load->library('encrypt'); 223 | $data = $this->ci->encrypt->decode($cookie); 224 | } 225 | 226 | $data = @unserialize($data); 227 | 228 | if (isset($data['id']) && isset($data['series']) && isset($data['key'])) { 229 | return $data; 230 | } 231 | 232 | return FALSE; 233 | } 234 | 235 | /** 236 | * Generate public/private key pair 237 | * 238 | * @return array 239 | */ 240 | private function generate_keys() { 241 | $public = hash($this->hash_algorithm, uniqid(rand())); 242 | $private = hash_hmac($this->hash_algorithm, $public, $this->ci->config->item('encryption_key')); 243 | 244 | return array($public, $private); 245 | } 246 | 247 | /** 248 | * Validate public/private key pair 249 | * 250 | * @param string $public 251 | * @param string $private 252 | * @return boolean 253 | */ 254 | private function validate_keys($public, $private) { 255 | $check = hash_hmac($this->hash_algorithm, $public, $this->ci->config->item('encryption_key')); 256 | return $check == $private; 257 | } 258 | 259 | } --------------------------------------------------------------------------------