├── 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 | }
--------------------------------------------------------------------------------