authentication OOP


We could have added functions to update a user and we could do that part easily by sending the userid via get method but we want the user himself would only be authorized to update his informations. It would be more secured if we stored the logged in user's id in session and used that one. So the first thing here would be to create a log in form.

LOG IN FORM

<?php
require_once('Configs.php');
require_once('Autolaod/Autolaod.php');
include ROOT.'admin/layouts/header.php';
?>

<div class="col-sm-4 col-sm-offset-4">
	<div class="panel panel-default">
		<div class="panel-heading text-center">
			<h3>Login To Dashboard</h3>			
		</div>
		<div class="panel-body">
			<?= Message(); ?>
			<form method="post" action="">
				<?= token::input() ?>
				<div class="form-group input-group">
					<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
					<input type="email" class="form-control" name="email" placeholder="Email">
				</div>
				<div class="form-group input-group">
					<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
					<input type="password" class="form-control" name="password" placeholder="Password">
				</div>
				<div class="form-group input-group">					
					<label for="remember"><input type="checkbox" name="remember_token" id="remember"> Remember me</label>
				</div>
				<div class="form-group">
					<input type="submit" value="Log In" class="btn btn-success pull-right">
					<a href="<?=HTTP?>" class="btn btn-default">Go to Site</a>
				</div>
			</form>	
		</div>
	</div>
</div>	

We created a login form here and included the global variables and autoloader along with the page header to add css files. We called the message class to display session messages and added csrf token value as well in the form. We also added a new post field remember_token that'll save the user's log in information in browser as cookie and a value in database too. We'll verify these two values when a user attempts to go to the login page. If those values are identical, we'll redirect the user straight to the page that's defined to be loaded after a user is logged in.

Let's add a key named cookie in Configs.php where the global variables are declared.

'cookies' => [
	'name' => 'remember_token',
]	

Lets create a class to handle all the cookie related operations.

COOKIE CLASS

<?php
class Cookie
{
	public static function put($key,$value,$expiry=1){
		if(!isset($key) && !isset($expiry)) return false;
		setcookie($key,$value,$expiry);
	}	


	public static function check($key){
		return isset($_COOKIE[$key]);
	}


	public static function get($key){
		if(!isset($key)) return false;
		if(self::check($key)){
			return $_COOKIE[$key];
		}
		return '';
	}

	public static function delete($key){
		if(!isset($key)) return false;
		if(self::check($key)){
			setcookie($key,'',-1);
		}
		return true;
	}
}	

put() function checks for the cookie key, value and expiry time of cookie. If all the values are passsed through the parameter correctly then the cokkie will be set.

check() function will check if the cookie has been set and returns bollean value.

get() function will return the cookie value if it exists or returns empty string.

delete() function will delete the cookie when called into action.

AUTHENTICATION TRAIT

Now, we can go to the authentican part. Let's create a trait instead of class in this case and we'll add use Authentiation; command in user controller file where we'll need it.

<?php
trait Authentication{

	private function authenticate($email='',$password='',$remember=null){
		
		$user = $this->getBy('email = ? AND status = 1',array($email),true);
		
		if(count($user)){
			$hash = $user->password;
			if(hash::decrypt($hash,$password)){
				if($remember === true){
					$key = hash::make(rand(1,10));
					Cookie::put(Config::get('cookies/name'),$key,(time()+(60*60*24*2)));
					$connect = new Database();
					$connect->update($this->table,['remember_token'=>$key],'id=?',array($user->id));
				}
				$this->redirectSuccess($user);
			}else{
				session::put('error',"<li class='fa fa-warning'><l/i> INVALID CRIDENTIALS");
				header('Location: '.HTTP.'admin/login.php');
				exit();
			}
		}else{
			session::put('error',"<li class='fa fa-warning'><l/i> UNAUTHORIZED ACCESS");
			header('Location: '.HTTP.'admin/login.php');
			exit();
		}
		
	}
}	

This function will accept all the parameters sent from the login page and searches for an email entry in users table identical to the email value sent via login page. The last value true returns 0 indexed array values from the result obtained using the SQL query.

If the email account exists then we'll fetch the user's saved password from database and verify it with the one sent via login page.

if the remember token is checked we'll create another encrypted value and save it as remember_me cookie value for the defined time being declared in seconds value 2 days in the example. Then make connection to the database and save it in users table's remeber token column in the logged in user's row and redirect the user using the function redirectSuccess().

If the email value matches but not the password, we'll redirect the user to the same log in page with session message set as invalid credentials while the user'll get back to the login page with unauthorized access in case the email value doesn't match or the user status is 0.

Another function in the trait would be the redirect success that'll store logged in user's details in session and redirect him to defined page.

private function redirectSuccess($user){
	session::put('authenticated_userid',$user->id);
	session::put('authenticated_username',$user->username);
	session::put('authenticated_email',$user->email);
	session::put('authenticated_usertype',$user->usertype);
	session::put('authenticated_image',$user->uploads);
	session::put('is_logged_in',TRUE);

redirect::to('admin/dashboard/');
}	

In cases where the user might opt to choose the remember_me token. The user might end the current session and come back to login before the cookie expires. In suh situations, we'll check for the cookie value remember_me in the user's browser. If the cookie exists, he will be redirected to the defined page directly. That's why, we saved a key is_logged_in true in session.

public function isLoggedIn(){
	
	$cookieName = Cookie::get(Config::get('cookies/name'));
		
	if(empty($cookieName)) return false;

	$connect = new Database();
	$user = $connect->select($this->table,$this->field,'remember_token=?',array($cookieName));
		
	if(!count($user)) return false;

	$user = $user[0];

	$this->redirectSuccess($user);
}	

Now, let's add a function is session that checks for the session logged in values. That'll prevent access to any of the application files without logging in.

public static function isLoggedIn(){
	if(!session::check('is_logged_in') || session::get('is_logged_in') !== TRUE){
		header('Location: login.php');
		exit();
	}
}	

We'll check if the session has value in is_logged_in key and the value must be true as well. Else, the user will be redirected to login page whenever he tries to access inner pages directly without goint to the login page. For that, we need to call this function on top in main.php.

Session::isLoggedIn();	

Now, we'll create a function in user controller that passes the login form values to Authentication trait that'll do the further processing.

public function isValidUser(){
	
	$email = Request::post('email');
	$password = Request::post('password');
	$remember = Request::post('remember_token');
	
	if(isset($remember)){
		$remember = true;
	}
		$this->authenticate($email,$password,$remember);
	}	

Now, we need to call the user controller class in the login page.

$obj = new User();
$obj->isLoggedIn();

if(Request::method() && Token::check(Request::post('csrf_token'))){
	
	$obj->isValidUser();
}

Once the cookie is set and the user intentionally logs out, we must be able to remove token value from database so that the user needs to login again the next time he wants to go through the application pages. For that, we'll create a function in user controller.

public function removeToken($id){
	$data['remember_token'] = null;
	return $this->save($data,$id);
}	

We simply replaced the remember_token with null value in the users remember_token field and update the user data. This function will run when a user logs out. Let's create that logout page then.

<?php
require_once('Configs.php');
require_once('Autoload/Autoload.php');
$id = session::get('authenticated_userid');
$obj = new User();
$obj->removeToken($id);
Cookie::delete(Config::get('cookies/name'));
session_destroy();
header('Location: login.php');	

So we included the global variables file and autoloader. Then we retrieved the user id of the user being logged in and then remove the remember_me token value from database and delete the cookie from browser too. Then all the messages stored in session will be deleted and the user will be redirected to the login page. Since the session value and remember_me token value from database has been deleted along with the cookie from browser, the user wont be able to go to inner pages without setting user values in session again.

PAGE RESTRICTION

Since we are working on authentication, we can also restrict general users to the users page where a user can be added or deleted. For that, we'll create a function in admin to check for restricted pages.

public static function checkAdmin($pages=array(),$currentPage)
{
	if (self::get('authenticated_usertype') !== 'admin'){
	
		if(in_array($currentPage, $pages)){
	
			$header = apache_request_headers()['Referer'];
	
			if(!empty($header)){
	
				self::put('error','Access denied');
				header('Location: '.$header);
	
			}else{
				return Redirect::to('admin/dashboard');
			}
		}
	}
}	

We'll make a call to this function with an array of restricted pages as one parameter and the requested page uri in the other. If the user type set in session doesn't have administrative authority, the the user will be redirected back to the page from where he sent the request also known as header referer. If no header referer is set then the uer will be sent to the dashboard. We need to add the restricted pages' uris in array and current page as parameters in that function.

$page = isset($_GET['page']) ? $_GET['page'] : 'dashboard';
session::checkAdmin(['add-user','display-user'],$page);