This is documentation for Kohana v2.3.x. For v3.x documentation, see http://kohanaframework.org/guide .

Table of Contents
StatusStub
TodoDefine what is a login 'role' or make a link to related doc
Todomake better examples

Auth Module

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.

Configuration

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.

	/**
	 * 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',);

Drivers

$config['driver'] sets the driver for your auth module. Kohana supports 2 drivers:

Database schema for MySQL

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

Hash methods

$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.

Salt pattern

$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!.

Remember me lifetime

$config['lifetime'] sets the auto-login (remember me) cookie lifetime, in seconds. The default lifetime is two weeks.

Session key

$config['session_key'] sets the session key that will be used to store the current user. It allows to have several instances of Auth.

Static users

$config['users'] is only used when using the File driver. It contains usernames (keys) and hashed passwords (values)/

Methods

auto_login()

auto_login() tries to login a user automatically. Only works if the user first logged in with the $remember option.

login()

login($username, $password, $remember = FALSE) validates a user's authentication credentials and logs a user in. It takes:

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()

force_login($username) forces a specific username to be logged in, without specifying a password

get_user()

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()

logout($destroy = FALSE)) logs a user out by removing the appropriate session variables.

// 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()

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';

change_password()

change_password(array & $array, $save = FALSE) Validates an array for a matching password and password_confirm field.

Example 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()

hash_password($password, $salt = FALSE) creates a hashed password from a plaintext password, inserting salt based on the configured salt pattern.

hash()

hash($str) performs a hash using the hash method set in the auth config file (e.g. sha1, md5).

find_salt()

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.

Examples

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.

Registration

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');
    }

Protecting Controllers

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

Login

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).

Login Example 1

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');
		}
	}
Login Example 2

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(); ?>

Database schema for ORM driver

Mysql schema

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;

PostgreSQL schema

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.');

Model Definitions

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);
	}
 
}
addons/auth.txt · Last modified: 2010/06/10 17:36 by cray