In the previous article User Handling – A SaaS Experience we added some basic functionality to our App to allow us to add, edit and disable users. In this article, we will add some authentication to our App, and allow only registered users to access it’s content. Eventually, we will add user level access, and custom permissions, but to begin, we will allow users to log in, and access everything we have created so far.

The first thing we are going to need is a login form! Our users will not be able to log in, if we don’t provide one. So we need to build a login View, and Controller, and as we are dealing with Users, we can use our User Model to handle authentication. We will start by creating a user login form in our Model.

<?PHP	// models/userstore.class.php -- additions only --
	
	public function getAuthenticationForm(){
		$Form = new AppForm('Authentication');
		$Form->add('label','labelUsername','Username','Username: ');
		$Username = $Form->add('text','Username');
		$Username->set_rule(array(
			'required' => array('error','Username is required'),
		));
		
		$Form->add('label','labelPassword','Password','Password: ');
		$Password = $Form->add('password','Password');
		$Password->set_rule(array(
			'required' => array('error','Password is required'),
		));
		
		$Form->add('submit','btnSubmit','Sign In');
		
		return $Form;
	}
?>

Our authentication form is quite simple, it contains two fields, username and password, and both are required. We have also added a “Sign In” button. We can now create our Login Controller and Login View. We also need to add our Login Controller and View to our dependency injection container.

<?PHP	// controllers/login.class.php

class Login extends Controller {
	
	public function __construct(){
		$this->Model(App::newUserStore());
	}
	
	public function index(){
		$this->View(App::newUserLoginView());
		$this->View->setForm($this->Model->getAuthenticationForm());
		$this->View->render();
	}
	
}	
	
?>
	
<?PHP	// views/userloginview.class.php

class UserLoginView extends View {
	private $Form;
	private $Content;
	
	public function __construct(){
		$this->setContent();
	}
	
	public function setForm($Form){
		$this->Form = $Form;
	}
	
	public function render(){
		echo $this->Content;
		if($this->Form->validate()){
			$this->Form = $this->Model->processAuthenticationForm();
		}
		$this->Form->render('*horizontal');		
	}
	
	public function setContent(){
		$Content = '<h1>Please Sign In</h1>';
		$this->Content = $Content;
	}
}
	
?>
	
<?PHP	// core/container.class.php
	
	public static function newLogin(){
		return new Login();
	}
		
	public static function newUserLoginView(){
		return new UserLoginView();
	}
?>

Everything we have done above, we have seen before. One thing that is a little different, is that I have used the Controller’s index() method to handle our authentication form, rather than creating a login() or authenticate() method. The reason for this, is that in general, this controller is going to be used to handle just out login form. We don’t need to add/edit/disable as we did with users. By using our index() method, our URL to login will simply be [our app URL]/Login rather than [our app URL]/Login/login. It is just a bit cleaner, and the login form is our default action for this controller.

If you update your autoloader, by running “composer dump”, you should see something similar to the image below when you visit our Login page.

Don’t worry too much about the appearance of this form, we will customise the look and feel of our App in a later article. We now have a form that displays and works correctly, however when it is submitted, we call a processAuthenticationForm() method on our Model, which is our UserStore. We need to implement this method now.

<?PHP	// models/userstore.class.php -- additions only --
	
	public function loadUser($Username, $Active = true){
		$User = $this->fetchOne("SELECT * FROM User WHERE Username = @Username AND Active = @Active", ['Username' => $Username, 'Active' => $Active]);
		if($User instanceof User){
			return $User;
		} else{
			return false;
		}
	}
	
	public function authenticateUser($User, $Password){
		$Authenticated = false;
		if(password_verify($Password, $User->Password)){
			$Authenticated = true;
		}
		return $Authenticated;
	}
	
	public function processAuthenticationForm(){
		if(!isset($_POST['Username'])){
			throw new UserException('Trying to authenticate user, but $_POST[Username] has not been set');
		}
		
		$GrantAccess = false;
		
		$User = $this->loadUser($_POST['Username']);
		if(!$User){
			$GrantAccess = false;
		} else{
			$GrantAccess = $this->authenticateUser($User, $_POST['Password']);
		}
		
		if($GrantAccess){
			$_SESSION['UserID'] = $User->getKeyId();
		}
		return $GrantAccess;		
	}
?>

We have created 2 helper methods, loadUser() and authenticateUser(). The loadUser() method accepts a Username and a ture/false value for $Active. It then uses this to look up the user with that username, then checks to ensure a User was returned. If a valid User was found, it is returned, if not the method will return false.

The authenticateUser() method accepts a User object, and a password, and uses the password library we have installed to verify that the password provided matches the password stored in the User object. If the passwords match, we return true, if they do not, we return false.

The processAuthenticationForm() method first checks to see if the $_POST[‘Username’] value has been set. If not, we throw a User Exception, as we cannot continue authenticating our user. If we have a Username set, we first set a $GrantAccess variable to false by default. We then attempt to load the User based on the Username provided. If we do not find a user, $GrantAccess is set to false, if we do find a User, we then set $GrantAccess to the result of our authenticateUser() method, remembering this will give us a true/false value depending on whether we were able to authenticate the password.

If $GrantAccess is set to true, we can then set a session to hold the UserId, we do this by setting $_SESSION[‘UserID’] to the value of $User->getKeyId(). We can then return $GrantAccess, the result of our authentication attempt.

If we were able to log the user in, we need to redirect the user from our login page to our dashboard. If the authentication fails, we need to alert the user to try to log in again. We can do this within our UserLoginView’s render() method.

<?PHP	// views/userloginview.class.php -- additions only --
		
	// -- updated --
	public function render(){
		if($this->Form->validate()){
			$Authenticated = $this->Model->processAuthenticationForm();
			if($Authenticated){
				echo "<script> window.location.assign('/Dashboard'); </script>";
				echo '<p>You have JavaScript disabled. You have been logged in. To continue <a href="/Dashboard">Click Here</a>';
			} else{
				$this->Form->add_error('error','We were unable to log you in. Please try again');
			}
		}
		
		echo $this->Content;

		$this->Form->render('*horizontal');		
	}
?>

We have updated our render() method to handle the result of our authentication attempt. If the login form validates, we set a variable $Authenticated to the result of out processAuthenticationForm() method, remembering we will get a true value if the user was logged in, or a false if not logged in. We then check the value of the $Authenticated variable, and if it is set to true, we use Javascript to redirect the user to the Dashboard. We also provide an HTML link pointing to our Dashboard in case users have Javascript disabled.

If the user is not authenticated, we add an error message to the login form, asking the user to try to log in again.

We now have the ability to allow a user to log in, and the ability to authenticate a user and set the session required to keep track of the user. We now need to implement some security in our App class to ensure only Authenticated users can access our App. We will break this down into a couple of steps.

<?PHP	// core/app.class.php -- additions only --
	
	// -- updated --
	public function __construct($Request){
		//Set error handlers
		$this->setHandlers();
		
		//Authenticate User
		$this->authenticate();

		//Process request
		$this->URL = $this->parseUrl($Request);
		$this->routeRequest();
	}
	
	private function authenticate(){
		if(isset($_SESSION['UserID'])){
			$Authenticated = $this->setCurrentUser($_SESSION['UserID']);
			if($Authenticated){
				echo 'Welcome '.self::$User->Name.' '.self::$User->Surname;
			} else{
				echo 'Not signed in';
			}
		} else{
			echo 'Not signed in';
		}
	}

	public function setCurrentUser($UserId){
		$UserStore = self::newUserStore();
		$User = $UserStore->fetchById($UserId);
		if($User instanceof User){
			self::$User = $User;
			return true;
		} else{
			return false;
		}
	}

?>

In this first step, I have created an authenticate() method which is called in the App constructor. This means that for every single page load, we will automatically run our authentication script. The authenticate() method is currently quite simple, it first checks to ensure that the $_SESSION[‘UserID’] value is set. If the value is set, we then run our new setCurrentUser() method. This method creates an instance of our UserStore, then attempts to retrieve our user from the datastore using the UserID we have been provided. The setCurrentUser() method will return true if it was successful, or false if the user was not found.

Our authenticate() method then checks whether setCurrentUser() was successful, and if so, we have output ‘Welcome’ with the current user’s first and surnames. If the setCurrentUser() method failed, or no UserID was provided, we echo a message that states the user was not logged in.

By using a user profile we created in the previous article, you can now try to log in, and if successful you will see the welcome message. In reality, we do not want a simple message, we want to either allow the user to view our content, or we need to redirect the user to our login page. We can do this in our authenticate() method.

<?PHP	// core/app.class.php -- additions only --
	
	// -- updated --
	private function authenticate(){
		if(isset($_SESSION['UserID'])){
			$Authenticated = $this->setCurrentUser($_SESSION['UserID']);
			if($Authenticated){
				echo 'Welcome '.self::$User->Name.' '.self::$User->Surname;
			} else{
				$this->redirectToLogin();
			}
		} else{
			$this->redirectToLogin();
		}
	}
	
	private function redirectToLogin(){
		echo "<script> window.location.assign('/Login'); </script>";
		echo '<p>You have JavaScript disabled. You have not logged in. To continue <a href="/Login">Click Here</a>';
		exit;
	}

We have updated our authenticate() method to call a redirectToLogin() method if authentication fails at any point. The redirectToLogin() method uses Javascript to redirect users to the login page. We also include an HTML link to the login page, in case a user does not have Javascript enabled. We then call PHP’s exit command to ensure no more code executes after the user is redirected.

Our users should now be automatically redirected to the login page should their authentication fail, or they have not logged in, but have tried to access the App. There is one problem with this however, and that is that our Login Controller, View, and Model all require our App to be running, so if we were to load the Login page, our authentication would fail, and we would be caught in an infinite loop of redirecting ourselves to the Login page, that we do not have access to. To solve this, we can create a type of Controller that is defined as external.

We will do this by adding a static property to our Controller class, from which all our controllers inherit. We will call the property $Internal, and give a default value of true. This will mark all of our Controllers as internal, and we can then override this in our Login controller and set it to false.

<?PHP	// core/controller.class.php -- additions only --
	
	class Controller {
	
		public static $Internal = true;
	
	}
?>
	
<?PHP	// controllers/login.class.php -- additions only --
	
	class Login extends Controller {
	
		public static $Internal = false;
	
	}
?>

We can now update our App’s __construct() method to first check to see if the Controller is an Internal controller before authenticating.

<?PHP	// core/app.class.php -- additions only --
	
	// -- updated --
	public function __construct($Request){
		//Set error handlers
		$this->setHandlers();
		
		//Parse Request
		$this->URL = $this->parseUrl($Request);

		//Authenticate User		
		if($this->requestIsInternal()){
			$this->authenticate();			
		}
		
		//Process request
		$this->routeRequest();
	}	

	private function requestIsInternal(){
		$Internal = true;
		$Controller = $this->URL[0];
		if(class_exists($Controller)){
			$Internal = $Controller::$Internal;
		}
		return $Internal;
	}

We have added a requestIsInternal() method to test if the requested page is an internal page. To test this, we first set a default to true, as if we cannot determine if the request is internal, we want to stop unauthorised users from viewing the requested page. Next, we find the name of the requested Controller from the URL variable generated previously by the parseURL() method. We then check to ensure the Controller class exists, and if it does, we set the value of our $Internal variable to the value we find within the Controller. Remember that all Controllers will inherit this value as true from the base Controller class, and at this stage only our Login Controller has overridden this value to false.

We then return our $Internal value, either true or false.

Our __construct() method now checks to see if the request is internal, using our new requestIsInternal() method, and if so, authenticates. If the user can’t be authenticated, they will be redirected to our Login page, which is now marked as Internal=false, so unauthenticated users will be able to use the login form.

You can test this by trying to access [our app url]/Dashboard without logging in, and you will be redirected to /Login. After logging in you will be redirected to the Dashboard as expected. In production, we don’t want our welcome message appearing on each page, so we just need to remove that line from our authenticate() method. you will notice this now leaves an empty IF block. Later we may choose to log the user’s access to each page by adding a logging method here.

We now have basic user authentication working! Any new Controller will automatically inherit this authentication unless we specify $Internal as false. Later, we will expand on our authentication and security, by adding user level roles and checking the user’s access to each page requested. For now though, we can determine if a user is logged in or not!

In the next article we will start developing a page management system that allows us to generate navigation menus, and more importantly will allow us to develop a user level, page based permissions system.