views:

1748

answers:

4

I'm aware of the risks of rolling your own user authentication scripts, but I'm also wary of using packages that don't seem to be actively maintained: the current version of PEAR LiveUser is almost a year old.

Please recommend (and argue the case for) an actively-maintained user-authentication library which can be integrated into an existing web project. It should ideally support various roles - anonymous users, registered users and administrators at various levels.

+3  A: 

I would suggest writing your own. An authentication library really isn't that hard to write. I wrote mine when I was learning PHP, and i'm still using it with little modification for every project. I can post the code here if it will help.

On the other hand, I do recommend CodeIgniter for a number of other libraries, including a database-based session Session Library.

Edit: Here is my Authentication class which has been serving me well. Keep in mind it was written while I was still learning PHP.

Usage:

Logging in a user / verifying login:

<?php
$Auth=new Auth();

$user=$_POST['user'];
$pw=$_POST['password'];

if (! $Auth->checkLogin($user,$pass))
{
    //Show login page here
}

else
{
    $Auth->login($user,$pass,true);
  //3rd paramater will set a cookie to 'remember' the user
}
?>

Restricting a page to be accessible only by those with a certain permission level:

<?php
//Restrict this page to only users with permission level 2 or above.
//Redirects to login page if not logged in, homepage if logged in but
//without sufficient permissions.

$Auth->restrict(2);     
?>

Logging out a user:

<?php
$Auth->logout(); //Kills session+cookie data and redirects to homepage
?>

Code: Disclaimer: This still has some CodeIgniter specific stuff which i didn't remove. In particular it uses my custom database class, and codeigniter's Session library for sessions.

<?php
/**
* Auth class. Used for all login/logout stuff.
*/
class Auth
{
    var $table, $userNameField, $passField, $miscField,$lastLoggedInField;
    var $loggedIn;
    var $homePageUrl, $loginPageUrl, $membersAreaUrl;
    var $obj;

    function Auth()
    {
     $this->table='your_table';
     //The fields below should be columns in the table above, which are used to
     //authenticate the user's credentials.
     $this->userNameField='username';
     $this->passField='password';

     //The numeric column which stores the permissions/level of each user:
     $this->lvlField='lvl'; 

     //The following are general columns in the database which are
     //stored in the Session, for easily displaying some information
     //about the user:
     $this->miscFields='id,first,email,lvl,verified,credits'; 

     /* If there is a no lastLoggedIn field in the table which is updated
               to the current DATETIME whenever the user logs in, set the next
              variable to blank to disable this feature. */

     $this->lastLoggedInField='last_login';

     $this->homePageUrl=site_url();
     $this->loginPageUrl=site_url('accounts/login');
     $this->membersAreaUrl=site_url();

     //This is a CodeIgniter specific variable used to refer to the base
     //CodeIgniter Object: 
     $this->obj=&get_instance();

     //This is my custom database library:
     $this->db=$this->obj->db;

     //All data passed on from a form to this class must be 
     // already escaped to prevent SQL injection.
      //However, all data stored in sessions is escaped by the class.

     if ($this->isLoggedIn())
      $this->refreshInfo();

    }
    function checkLogin($user, $pass)
    {
     $sql="SELECT $this->miscFields FROM $this->table 
     WHERE $this->userNameField='$user' AND $this->passField='$pass'";
     $query=$this->db->query($sql);
     return ($query->num_rows() ===1);
    }

    function isSessLoggedIn()
    {
     if ($this->loggedIn==='yes')
      return true;
     $user=escapeStr($this->obj->session->userdata('user'));
     $pass=escapeStr($this->obj->session->userdata('pass'));

     if ($this->checkLogin(escapeStr($user),escapeStr($pass),0))
     {
      $this->loggedIn='yes';
      return true;
     } 
     else
     {
      $this->loggedIn=FALSE;
      return false;
     }
    }

    function isCookieLoggedIn()
    {
     if (! array_key_exists('user',$_COOKIE) || ! array_key_exists('pass',$_COOKIE))
      return false;
     $user=escapeStr($_COOKIE['user']);
     $pass=escapeStr($_COOKIE['pass']);
     if ($this->checkLogin($user,$pass))
      $loggedIn=TRUE;
     else
      $loggedIn=FALSE;
     if ($loggedIn && ! $this->isSessLoggedIn())
     {
      $sql="SELECT $this->passField FROM $this->table 
      WHERE $this->userNameField='$user' LIMIT 1";
      $query=$this->db->query($sql);
      $pass=$query->getSingle($this->passField);
      $this->login($user,$pass);
     }
     return $loggedIn;
    }

    function isLoggedIn()
    {
     return ($this->isSessLoggedIn() || $this->isCookieLoggedIn());
    }



    function login($user, $pass,$remember=FALSE)
    {

     if ($this->isSessLoggedIn())
      return false;

     if (! $this->checkLogin($user,$pass))
      return false;

     $this->obj->session->set_userdata('user',$user); 
     $this->obj->session->set_userdata('pass',$pass);

     $sql="SELECT $this->miscFields FROM $this->table 
     WHERE $this->userNameField='$user' && $this->passField='$pass'";
     $query=$this->db->query($sql);
     $fields=explode(',',$this->miscFields);
     foreach ($fields as $k=>$v)
     {
      $fieldName=$v;
      $fieldVal=$query->getSingle($v);
      $this->obj->session->set_userdata($fieldName,$fieldVal);
     }


     if ($this->lastLoggedInField !='')
     {
      $sql="UPDATE $this->table SET 
      $this->lastLoggedInField=NOW(),num_logins=num_logins + 1 
      WHERE $this->userNameField='$user' && $this->passField='$pass'";
      $this->db->query($sql);
     }

     if ($remember)
      $this->setCookies();
     return true;
    }


    function logout($redir=true)
    {
     if (! $this->isLoggedIn())
      return false;

     $this->obj->session->sess_destroy();

     if ($this->isCookieLoggedIn())
     {
      setcookie('user','', time()-36000, '/');
      setcookie('pass','', time()-36000, '/');
     }
     if (! $redir)
      return;

     header('location: '.$this->homePageUrl);
     die;
    }


    function restrict($minLevel)
    {
     if (! is_numeric($minLevel) && $minLevel!='ADMIN')
      return false;


     //URL of the page the user was trying to access, so upon logging in
     // he is redirected back to this url.
     $url=$this->obj->uri->uri_string();
     if (! $this->isLoggedIn())
     {
      $this->obj->session->set_userdata('redirect_url',$url);
      header('location: '.$this->loginPageUrl);
      die;
     }

     if ($this->obj->session->userdata($this->lvlField) < $minLevel)
     {
      header('location: '.$this->membersAreaUrl);
      die;
     }
     return true;
    }


    function setCookies()
    {
     if (! $this->isSessLoggedIn())
     {
      return false;
     }
     $user=$this->obj->session->userdata('user');
     $pass=$this->obj->session->userdata('pass');

     @setcookie('user',$user, time()+60*60*24*30, '/');
     @setcookie('pass',$pass, time()+60*60*24*30, '/');
     return true;
    }


    //This function refreshes all the info in the Session, so if a user changed
    //his name, for example, his name in the Session is updated
    function refreshInfo()
    {
     if (! $this->isLoggedIn())
      return false;
     $id=trim($this->obj->session->userdata('id'));
     $sql="SELECT $this->passField,$this->userNameField, 
     $this->miscFields FROM $this->table WHERE id='$id' LIMIT 1";
     $query=$this->db->query($sql);
     $info['pass']=$query->getSingle($this->passField);
     $info['user']=$query->getSingle($this->userNameField);
     $fields=explode(',',$this->miscFields);
     foreach ($fields as $k=>$v)
     {
      $info[$v]=$query->getSingle($v);
     }

     //The following variables are used to determine wether or not to
     //set the cookies on the users computer. If $origUser matches the
     //cookie value 'user' it means the user had cookies stored on his 
     //browser, so the cookies would be re-written with the new value of the
     //username.
     $origUser=$this->obj->session->userdata('user');
     $origPass=$this->obj->session->userdata('pass');
     foreach ($info as $k=>$v)
     {
      $this->obj->session->set_userdata($k,$v);
     }

     if (array_key_exists('user',$_COOKIE) && array_key_exists('pass',$_COOKIE))
     {
      if ($_COOKIE['user']==$origUser && $_COOKIE['pass']==$origPass)
       $this->setCookies();
     }
     return true;
    }


    function isAdmin()
    {
     if (! $this->isLoggedIn())
      return false;
     $lvl=$this->obj->session->userdata('lvl');

     return ($lvl >= 2);
    }  


    function isVerified()
    {
     return ($this->obj->session->userdata('verified')=='1');
    }
}


/**
 * Used for quickly doing mysql_real_escape() and trim() on a string.
 */ 
function escapeStr($str)
{
    return trim(mysql_real_escape_string($str));
}
?>
Click Upvote
I wouldn't mind seeing your take on user authentication. Sometimes with PHP its hard to know if I'm doing things write (even after all these years) because I rarely see others code that isn't for publication in a book.
Forrest Marvez
It looks pretty good, however my only concern would be the way passwords are stored, but as long as you're sure your database isn't going to be looked at then it really doesn't matter too much.
Forrest Marvez
Actually passwords are md5(0ed in the database and are md5()ed before being passed to the checkLogin() function of this class. apologies, i should've mentioned that
Click Upvote
Hello your code is great for helping me learn some. I am curious, I see the isSessLoggedIn() and isCookieLoggedIn() seem to call checkLogin(user, pass), checklogin() does a mysql query, so is this checklogin query ran on every page load? Thanks for any help
jasondavis
+4  A: 

It looks to me like PEAR hasn't changed much because it's stable. I wouldn't be afraid of using it.

EdgarVerona
+4  A: 

It sounds like what you want is a user control library, rather than an authentication library.

For example, in the Zend Framework there are two classes: Zend_Auth (which handles user authentication: logins (e.g. simple database tables to OpenID)) and Zend_Acl (which handles the user access side of things).

I quite like the ZF classes - I haven't tried using them outside of a ZF project but most of their classes can so give it a try. Even if you decide to build your own they'd be useful for reference.

Ross
A: 

When using md5(); to store passwords on a table in your mysql database.

keep in mind the same proccess will be required when checking the entered password from the login form, aganst the one in the database.

Another example of a hash generating function is

sha1();

u could always wrap the 2 functions and create a simple hash generation method

$string = md5(sha1($string));

return $string; // RETURNED HASHED VALUE

U COULD ALWAYS USE ANOTHER METHOD SUCH AS A SALT, OR, RANDOM PASSWORD GENERATOR!

Doing `md5(sha1($string))` would only increase the collision rate and make your application less secure. You should use only sha1, or if that is not enough try sha 256 or sha 512 algorithms
Petah