This is documentation for Kohana v2.3.x.
Status | Stub |
---|---|
Todo | Define what is a login 'role' or make a link to related doc |
Todo | make better examples |
The Kohana Auth module provides an easy-to-use API for basic website authentication (users) and authorization (roles). It also offers built-in support for user session creation, auto-login and password encryption. The Auth module is driver-based, which makes it possible to plugin to various authentication sources – currently Database and File drivers are provided. It is outside of the scope of the Auth module to include fully-functional login, registration or password recovery forms – these should be implemented by the developer as per application requirements.
Note: In order to log a user in, a user must have at least the login
role. You may create and assign any other role to your users.
See modules for more information on using modules.
To use the Auth module, enable it in application/config/config.php
by uncommenting the following line in the $config['modules']
array.
$config['modules'] = array ( MODPATH.'auth', // Authentication ... );
Although you can use the default configurations provided with the module, it is highly-recommended to create application specific copies of the following configuration files and modify them accordingly. See configuration and filesystem for more information.
When setting the salt_pattern option, the number of positions must be greater than or equal to your salt length. So if you're using a salt that is 10 characters or less, the pattern below would work. Otherwise, your salt will not be properly stored in the password field.
modules/auth/config/auth.php
to application/config/auth.php
/** * Driver to use for authentication. By default, File and ORM (default) are available. */ $config['driver'] = 'ORM'; $config['hash_method'] = 'sha1'; $config['salt_pattern'] = '1, 3, 5, 9, 14, 15, 20, 21, 28, 30'; // this should always be changed $config['lifetime'] = 1209600; $config['users'] = array( 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',);
system/config/cookie.php
and system/config/session.php
in your application/config
directory and modify the values accordingly. See deployment for more information.
$config['driver']
sets the driver for your auth module. Kohana supports 2 drivers:
Database schema for PostgreSQL
To store users in non-default database, set predefined database group in models. For example: models/user_token.php
with users
group.
class User_Token_Model extends Auth_User_Token_Model { protected $db = 'users'; // This class can be replaced or extended } // End User Token Model
$config['hash_method']
sets the type of hash to use for passwords (e.g. sha1, md5). Any algorithm supported by the hash function can be used here. Note that the length of your password is determined by the hash type and the number of salt characters defined. Default is sha1.
$config['salt_pattern']
defines the hash offsets to insert the salt at. The values are to be in ascending numerical order, and that the largest number is less than the length of your chosen hash algorithm. (40 for sha1, 32 for md5). You must have at least one salt offset, and can add up to length/2 offsets (SHA1 = 40 so 40/2 = 20 maximum). The password hash length will be increased by the total number of offsets so for the default settings of SHA1 and 10 offsets, the password field will have a length of 50. This should always be changed as soon as possible. Changing your salt pattern will invalidate every existing users' password!.
$config['lifetime']
sets the auto-login (remember me) cookie lifetime, in seconds. The default lifetime is two weeks.
system/config/cookie.php
and system/config/session.php
in your application/config
directory and modify the values accordingly. See deployment for more information.
$config['session_key']
sets the session key that will be used to store the current user. It allows to have several instances of Auth.
$config['users']
is only used when using the File
driver. It contains usernames (keys) and hashed passwords (values)/
auto_login()
tries to login a user automatically. Only works if the user first logged in with the $remember option.
login($username, $password, $remember = FALSE)
validates a user's authentication credentials and logs a user in. It takes:
$username
username to log in$password
password to check against (plaintext - not hashed)$remember
enable auto-login (default FALSE). If true, it keeps the user's login information, so the user can be logged in automatically, whenever he comes back, using auto_login(). The default duration of the auto-login cookie is two weeks, and can be set in the Auth config file.Note: login() checks if the given User has the role 'login'. So every user you want to be able to log into your system has to have at least the 'login' role. An user can have more than one role.
$user = ORM::factory('user', $post->username); $this->auth = new Auth(); if ( ! $user->loaded) { $post->add_error('username', 'not_found'); } elseif ($this->auth->login($user, $post->password)) { url::redirect($this->session->get_once('referrer')); } else { $post->add_error('password', 'incorrect_password'); }
force_login($username)
forces a specific username to be logged in, without specifying a password
get_user()
returns the currently logged in user, or FALSE.
$user_id = Auth::instance()->get_user()->id; $username = Auth::instance()->get_user()->username;
Example: Displaying the username in a view
<?php echo html::specialchars(Auth::instance()->get_user()->username); ?>
logout($destroy = FALSE))
logs a user out by removing the appropriate session variables.
$destroy
If true, completely destroy the session; if false, do not delete the session, but only remove the authentication information from the session (default FALSE).// A logout function in a controller public function logout() { $this->auth = new Auth(); $this->auth->logout(); url::redirect('welcome'); } // or using a a static method public function logout() { Auth::instance()->logout(); url::redirect('welcome'); }
logged_in($role = NULL)
check if there is an active session. Optionally allows checking for specific roles.
echo (Auth::instance()->logged_in()) ? 'logged_in' : 'logged_out';
$role
a role or an array of roles to check the user against(default NULL).
change_password(array & $array, $save = FALSE)
Validates an array for a matching password and password_confirm field.
$array
values to check$save
save the user if trueExample 1: Changing the password only. Use this when you want to perform specific actions only on a sucessful password change or don't wish to save the change immediately.
$post = $this->input->post(); if ($user->change_password($post)) { $user->save(); // Other tasks you want to perform } else { if ($post->errors()) { // Handle validation errors } else { // Handle non-validation errors } }
Example 2: Changing the password and saving the user object. Use this when you want to save the change straight away. It allows you to have more concise code which only handles the error.
$post = $this->input->post(); if (!$user->change_password($post, true)) { if ($post->errors()) { // Handle validation errors } else { // Handle non-validation errors } }
Example 3: Changing the password, saving the user object and redirecting to another page. Upon successful changing of the password, the user will be sent to a different page (in this case 'user/change_password_success'
$post = $this->input->post(); if (!$user->change_password($post, 'user/change_password_success')) { if ($post->errors()) { // Handle validation errors } else { // Handle non-validation errors } }
hash_password($password, $salt = FALSE)
creates a hashed password from a plaintext password, inserting salt based on the configured salt pattern.
$password
plaintext password$salt
hashed password string
hash($str)
performs a hash using the hash method set in the auth config file (e.g. sha1, md5).
find_salt($password)
finds the salt from a password, based on the configured salt pattern. Needed for doing password checks yourself in situations other than login.
To start using Auth module you have to create users
, roles
, roles_users
tables with required fields in users
: username, password, logins, last_login at the minimum (and so on with your own fields) – use the SQL code in the “Database schema for ORM driver” section below.
Example - Create User, Login, and Redirect:
// grab relevant $_POST data $username = $this->input->post('username'); $password = $this->input->post('password'); // instantiate User_Model and set attributes to the $_POST data $user = ORM::factory('user'); $user->username = $username; $user->password = Auth::instance()->hash_password($password); // if the user was successfully created... if ($user->add(ORM::factory('role', 'login')) AND $user->save()) { // login using the collected data Auth::instance()->login($username, $password); // redirect to somewhere url::redirect('user/profile'); }
To make sure that certain controllers can be accessed by the appropriate users you can do the following:
function __construct(){ parent::__construct(); $this->session= Session::instance(); $authentic=new Auth; if (!$authentic->logged_in()){ $this->session->set("requested_url","/".url::current()); // this will redirect from the login page back to this page url::redirect('/user/login'); }else{ $this->user = $authentic->get_user(); //now you have access to user information stored in the database } }
To allow only users with a certain role (admin) to a model, change 'if (!$authentic→logged_in()){' to if (!$authentic→logged_in(“admin”)){
function __construct(){ parent::__construct(); $this->session= Session::instance(); $authentic=new Auth; if (!$authentic->logged_in('admin')){ $this->session->set("requested_url","/".url::current()); // this will redirect from the login page back to this page/ url::redirect('/user/login'); }else{ $this->user = $authentic->get_user(); //now you have access to user information stored in the database } }
When you implement your login form, it is very helpful if the names of the form fields match the names of the database fields (username
and password
).
user login, redirects to a page stored in the session if a user is logged in with the proper role. If the user is logged in but does not have the role required the user will be redirected to a page explaining this. If the user is not logged in the user will be redirected to a login page. If there is POST data this function is called from a login form so the data is verified and the user is logged in and again redirected to this function, but as the user is now logged in, redirected to the page stored in the session variable. Otherwise the user can try to login again.
/* main login function, return to page if logged in with proper credentials */ public function login($role="") { if (Auth::instance()->logged_in($role)) { url::redirect($this->session->get("requested_url")); //return to page where login was called } else { if (Auth::instance()->logged_in()){ $this->template->title="No Access"; $this->template->content=new View('user/noaccess'); }else{ $this->template->title="Please Login"; $this->template->content= new View('user/login'); } } $form = $_POST; if($form) { // Load the user $user = ORM::factory('user', $form['username']); // orm user object or $form['username'] could be used Auth::instance()->login($user->username, $form['password']); url::redirect('/user/login'); } }
This example uses the User ORM model, which provides better validation than using the “Auth::instance()→login()” method (the code from example 1 will fail if username is missing or doesn't exist in the database).
public function login() { //Check if already logged in if (Auth::instance()->logged_in('login')) { url::redirect('index'); } else if (Auth::instance()->logged_in()) { url::redirect('accessdenied'); //User hasn't confirmed account yet } //Initialize template and form fields $view = new View('login'); $view->username = ''; $view->password = ''; //Attempt login if form was submitted if ($post = $this->input->post()) { if (ORM::factory('user')->login($post)) { url::redirect($this->session->get('requested_url')); } else { $view->username = $post['username']; //Redisplay username (but not password) when form is redisplayed. $view->message = in_array('required', $post->errors()) ? 'Username and password are required.' : 'Invalid username and/or password.'; } } //Display login form $view->render(TRUE); }
And your view would look something like this:
<h1>Welcome! Please log in.</h1> <?php if (isset($message)): ?> <div class="error" style="color: red;"> <?=$message?> </div> <?php endif; ?> <?php echo form::open('login'); ?> <?php echo form::label('username', 'Username:'); ?> <?php echo form::input('username', $username); ?> <br /> <?php echo form::label('password', 'Password:'); ?> <?php echo form::password('password'); ?> <br /> <?php echo form::submit('submit', 'Login'); ?> <?php echo form::close(); ?>
CREATE TABLE IF NOT EXISTS `roles` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `description` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation'); INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.'); CREATE TABLE IF NOT EXISTS `roles_users` ( `user_id` int(10) UNSIGNED NOT NULL, `role_id` int(10) UNSIGNED NOT NULL, PRIMARY KEY (`user_id`,`role_id`), KEY `fk_role_id` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `email` varchar(127) NOT NULL, `username` varchar(32) NOT NULL DEFAULT '', `password` char(50) NOT NULL, `logins` int(10) UNSIGNED NOT NULL DEFAULT '0', `last_login` int(10) UNSIGNED, PRIMARY KEY (`id`), UNIQUE KEY `uniq_username` (`username`), UNIQUE KEY `uniq_email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `user_tokens` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` int(11) UNSIGNED NOT NULL, `user_agent` varchar(40) NOT NULL, `token` varchar(32) NOT NULL, `created` int(10) UNSIGNED NOT NULL, `expires` int(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_token` (`token`), KEY `fk_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `roles_users` ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE; ALTER TABLE `user_tokens` ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
CREATE TABLE roles ( id serial, "name" varchar(32) NOT NULL, description text NOT NULL, CONSTRAINT roles_id_pkey PRIMARY KEY (id), CONSTRAINT roles_name_key UNIQUE (name) ); CREATE TABLE roles_users ( user_id integer, role_id integer ); CREATE TABLE users ( id serial, email varchar(127) NOT NULL, username varchar(32) NOT NULL, "password" varchar(50) NOT NULL, logins integer NOT NULL DEFAULT 0, last_login integer, CONSTRAINT users_id_pkey PRIMARY KEY (id), CONSTRAINT users_username_key UNIQUE (username), CONSTRAINT users_username_check CHECK (username ~* '[a-zA-Z0-9_.]'), CONSTRAINT users_email_key UNIQUE (email), CONSTRAINT users_logins_check CHECK (logins >= 0) ); CREATE TABLE user_tokens ( id serial, user_id integer NOT NULL, user_agent varchar(40) NOT NULL, token character varying(32) NOT NULL, created integer NOT NULL, expires integer NOT NULL, CONSTRAINT user_tokens_id_pkey PRIMARY KEY (id), CONSTRAINT user_tokens_token_key UNIQUE (token) ); CREATE INDEX user_id_idx ON roles_users (user_id); CREATE INDEX role_id_idx ON roles_users (role_id); ALTER TABLE roles_users ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, ADD CONSTRAINT role_id_fkey FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE; ALTER TABLE user_tokens ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; INSERT INTO roles (name, description) VALUES ('login', 'Login privileges, granted after account confirmation'); INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.');
Auth is dependent on alternate unique keys being defined both in your database schema (see above) and in your model definitions. Specifically, it expects your User_Model to allow lookups on the `username` column, and Role_Model to allow lookups on the `name` column. So:
// in models/role.php class Role_Model extends ORM { protected $has_and_belongs_to_many = array('users'); public function unique_key($id = NULL) { if ( ! empty($id) AND is_string($id) AND ! ctype_digit($id) ) { return 'name'; } return parent::unique_key($id); } } // and, in models/user.php class User_Model extends ORM { protected $has_and_belongs_to_many = array('roles'); public function unique_key($id = NULL) { if ( ! empty($id) AND is_string($id) AND ! ctype_digit($id) ) { return 'username'; } return parent::unique_key($id); } }