I've wrote this class in PHP for my future projects:
<?php
/**
* Auth
* This class is used to securely authenticate and register users on a given website.
* This class uses the crypt() function, and PDO database engine.
*
* @package Class Library
* @author Truth
* @copyright 2011
* @version 1.00
* @access public
*/
class Auth {
const HOST = 'localhost'; //Holds the database host. For most cases that would be 'localhost'
const NAME = 'users'; //Holds the database name for the class to use. Assums 'users', change if needed.
const USER = 'root'; //Holds the username for the database. CHANGE IF NEEDED!!
const PASS = ''; //Holds the password for the database. CHANGE IF NEEDED
const TABLE = 'users'; //Holds the name of the table. CHANGE IF NEEDED
#NOTE, THE USER/PASS COMBO MUST HAVE SUFFICIENT PRIVLIGES FOR THE DATABASE IN NAME.
/**
* @var Auth::$db
* Holds pointer to the PDO object
*/
protected static $db;
/**
* @var Auth::$user
* Holds username information in the instance object
*/
protected $user;
/**
* @var Auth::$pass
* Holds password information in the instance object
*/
protected $pass;
/**
* @var Auth::$hash
* Holds the hashed password ready for database storage
*/
protected $hash;
/**
* Auth::__construct()
*
* @param string $user
* @param string $pass
* @return void
*/
public function __construct($user = "", $pass = "") {
if (empty($user) || empty($pass)) {
throw new Exception("Empty username or password.");
}
$this->user = $user;
$this->pass = $pass;
}
/**
* Auth::hash()
* Pass a user/password combination through the crypt algorithm and return the result.
*
* @return string hash Returns the complete hashed string.
*/
private function hash() {
$this->hash = crypt($this->pass, '$2a$10$'.sha1($this->user));
return $this->hash;
}
/**
* Auth::getUname()
* Return the username
*
* @return string The username
*/
public function getUname() {
return $this->user;
}
/**
* Auth::getHash()
* Check if there's a hash, if not, make one, then return the hash.
*
* @return string Hashed password
*/
public function getHash() {
if (empty($this->hash)) {
$this->hash();
}
return $this->hash;
}
public function __toString() {
return $this->getHash();
}
/**
* Auth::databaseConnect()
* Establish connection to database and store the connection on self::$db.
*
* @param PDO $pdo an optional PDO object to have an existing connection instead of a new one
* @return void
*/
public static function databaseConnect($pdo = null) {
#The class accepts an external
if (is_object(self::$db)) {
#Should the connection already been established, an exception will be thrown.
#If you want to start the connection yourself, make sure that this function is called before any other class functions.
throw new Exception('Could not complete operation. Connection was already established.');
}
if (is_object($pdo)) {
if (!($pdo instanceof PDO)) {
throw Exception('Illegal Argument: Supplied argument is not a PDO object.');
}
#The function accepts an external PDO object as an argument.
#WARNING: Do not use an object other then a PDO object, as it might cause unexpected results.
self::$db = $pdo;
return 0;
}
$dsn = "mysql:host=".self::HOST.";dbname=".self::NAME;
try {
self::$db = new PDO($dsn, self::USER, self::PASS); //Connect to the database, and store the pdo object.
self::$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e) {
throw new Exception("There have been an error in the connection: ". $e->getMessage());
}
#Here, connection is established.
}
/**
* Auth::databaseCreate()
* Create a default table (named self::TABLE) on the database.
* Only use from within an installation page or a try/catch statement
*
* @return void
*/
public static function databaseCreate() {
if (!is_object(self::$db)) {
self::databaseConnect();
}
$table = self::TABLE;
$query = <<<EOQ
CREATE TABLE $table(
`uname` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'Holds usernames (who also act as salt)',
`phash` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'Holds hashed passwords',
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique user ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin COMMENT='Default user table' AUTO_INCREMENT=1 ;
EOQ;
//var_dump(self::$db);
$stmt = self::$db->prepare($query);
$stmt->execute();
}
/**
* Auth::registerUser()
* Insert a user into the database.
*
* @param Auth $auth The $auth object in question. Escaping is not needed.
* @return void
*/
public static function registerUser(Auth $auth) {
if (!is_object(self::$db)) {
self::databaseConnect();
}
try {
$query = "INSERT INTO ".self::TABLE." (`uname`, `phash`, `id`) VALUES (:uname, :phash, NULL)";
$stmt = self::$db->prepare($query);
$stmt->bindValue(':uname', $auth->getUname());
$stmt->bindValue(':phash', $auth->getHash());
$stmt->execute();
}
catch (PDOException $e) {
echo "There has been an error registering: ". $e->getMessage();
}
}
/**
* Auth::validateAgainstDatabase()
* Validate a user against the database.
* Exceptions detailing the potential error will be thrown. Make sure to catch them!
*
* @param Auth $auth The Auth object in question. No escaping needed.
* @return bool $success Whether the user is valid or not.
*/
public static function validateAgainstDatabase(Auth $auth) {
if (!is_object(self::$db)) {
self::databaseConnect();
}
try {
$query = "SELECT `uname`, `phash` FROM ".self::TABLE." WHERE `uname` = :uname";
$stmt = self::$db->prepare($query);
$stmt->bindValue(':uname', $auth->getUname());
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
throw new Exception('Username does not exist in the system.');
}
if ($row['phash'] != $auth->getHash()) {
throw new Exception('Password does not match username.');
}
return true;
}
catch (PDOException $e) {
echo "There was an error during the fetching: ". $e->getMessage();
}
}
}
?>
What do you guys think? Can it be improved? Does it look good? Anything to consider?
Will appreciate any review. Thanks in advance :)