How to write and design a simple (but still proper and secure) login class for PHP?
Currently I'm checking whether there is a login request (user entered data into login form) or the session is already existent and contains $_SESSION['authenticated'] == TRUE;
. If both checks failed the user is not (properly) logged in. In code:
class Auth {
private function __construct() {
// new login
if(isset($_POST['login'], $_POST['username'], $_POST['password']) && validCSRFToken()) {
$user = $_POST['username'];
$password = $_POST['password'];
if(($row = querySingle('
SELECT `id`, `password`
FROM `users` u
WHERE `nick` = "?"',
$user)))
// Crypt::hash checks the salted password
&& Crypt::hash($password, $row['password'])) {
// credential change (guest -> user) regenerate session id
session_regenerate_id(TRUE);
$_SESSION['authenticated'] = TRUE;
$_SESSION['userid'] = $row['id'];
$_SESSION['user'] = $user;
// prevent 'your browser has to re-send some data to display this page'
redirect($_SERVER['REQUEST_URI']);
} else {
$this->logout();
throw new AuthException('login failed. wrong user/password');
}
} else if($_SESSION['authenticated'] && $_SESSION['userid'] > 0 && !empty($_SESSION['user'])) {
// user already logged in, nothing to see here, move on
} else {
// not logged in!
throw new AuthException('not logged in. please login');
}
}
public function logout() {
$this->authenticated = FALSE;
$this->user = '';
$this->userid = 0;
$this->session->destroy();
$this->session->regenerate_id(TRUE);
}
}
An object of the Auth
class has to be created on every page that requires authentication from users:
try {
$auth = new Auth();
/*
* rest of the page
*/
} catch(AuthException $ex) {
// something went wrong during login
displayError();
displayLoginForm();
exit; // halt further execution of script
}
Is this the right way to do it? Do you see anything (seriously) flawed with this approach? Can users impersonate other users? Users getting logged out randomly? Users getting logged out by malicious attackers? (although CSRF checks are in place). I'm afraid, I might be missing something obvious.
Any improvements apreciated, thanks!
(Please don't suggest using a PHP framework like CodeIgniter, Zend Framework, Kohana, etc.)