Authentication

CakePHP 4 Authentication Using Authentication Plugin

This tutorial will guide you to create authentication for your CakePHP 4 project. The CakePHP Authentication plugin is used in this tutorial. This tutorial covers the basic authentication process (login, logout, hashing password). You'll need a composer installed in your computer to download the Authentication plugin.


Navigate your CLI to your application directory and execute the following command. Use the composer to install the Authentication Plugin:

composer require cakephp/authentication:^2.0


Adding Password Hashing at .../src/Model/Entity/User.php add the following:

<?php
namespace App\Model\Entity;

use Authentication\PasswordHasher\DefaultPasswordHasher; // Add this line
use Cake\ORM\Entity;

class User extends Entity
{
    // Others code.

    // Add this method
    protected function _setPassword(string $password) : ?string
    {
        if (strlen($password) > 0) {
            return (new DefaultPasswordHasher())->hash($password);
        }
    }
}

In .../src/Application.php, add the following imports and authentication interface on Application class (Attention: Do not redeclare the public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue) - The public function already exists and need some modification only:

use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ServerRequestInterface;

use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;

class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{

	public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
	{
		$middlewareQueue
			->add(new ErrorHandlerMiddleware(Configure::read('Error')))
            ->add(new AssetMiddleware([
                'cacheTime' => Configure::read('Asset.cacheTime'),
            ]))
			->add(new RoutingMiddleware($this))
			->add(new AuthenticationMiddleware($this));

		return $middlewareQueue;
	}
	
	public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
	{
		$authenticationService = new AuthenticationService([
			'unauthenticatedRedirect' => '/folderName/users/login',
			'queryParam' => 'redirect',
		]);

		// Load identifiers, ensure we check email and password fields
		$authenticationService->loadIdentifier('Authentication.Password', [
			'fields' => [
				'username' => 'email',
				'password' => 'password',
			]
		]);

		// Load the authenticators, you want session first
		$authenticationService->loadAuthenticator('Authentication.Session');
		// Configure form data check to pick email and password
		$authenticationService->loadAuthenticator('Authentication.Form', [
			'fields' => [
				'username' => 'email',
				'password' => 'password',
			],
			'loginUrl' => '/folderName/users/login',
		]);

		return $authenticationService;
	}

In .../src/Controller/AppController.php class add the following code:

public function initialize(): void
{
    parent::initialize();
    $this->loadComponent('RequestHandler');
    $this->loadComponent('Flash');

    // Add this line to check authentication result and lock your site
    $this->loadComponent('Authentication.Authentication');

In .../src/Controller/UsersController, add the following code:

public function beforeFilter(\Cake\Event\EventInterface $event)
{
    parent::beforeFilter($event);
    $this->Authentication->addUnauthenticatedActions(['login']);
}

public function login()
{
    $this->request->allowMethod(['get', 'post']);
    $result = $this->Authentication->getResult();
    if ($result->isValid()) {
        $redirect = $this->request->getQuery('redirect', [
            'controller' => 'Users',
            'action' => 'index',
        ]);

        return $this->redirect($redirect);
    }
    if ($this->request->is('post') && !$result->isValid()) {
        $this->Flash->error(__('Invalid username or password'));
    }
}

public function logout()
{
    $result = $this->Authentication->getResult();
    if ($result->isValid()) {
        $this->Authentication->logout();
        return $this->redirect(['controller' => 'Users', 'action' => 'login']);
    }
}

Create the template logic for login action:

<div class="users form">
    <?= $this->Flash->render() ?>
    <h3>Login</h3>
    <?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('Please enter your username and password') ?></legend>
        <?= $this->Form->control('email', ['required' => true]) ?>
        <?= $this->Form->control('password', ['required' => true]) ?>
    </fieldset>
    <?= $this->Form->submit(__('Login')); ?>
    <?= $this->Form->end() ?>

    <?= $this->Html->link("Add User", ['action' => 'add']) ?>
</div>

Navigate to localhost/myCake4/users and it will redirect you to the login page as follows:


To allow the add/register new user, navigate to usersController.php and modify the public function before filter. Allow the 'add' page to render without authentication.

public function beforeFilter(\Cake\Event\EventInterface $event)
{
    parent::beforeFilter($event);
    $this->Authentication->addUnauthenticatedActions(['login','add']);
}

Register a new user and log in using the created account. The password should be hashed and secure. Once logged in, it will redirect to the user index page as follows:


To generate the logout button at index page, navigate to .../templates/User/index.php and add the following codes:

<?= $this->Html->link(__('Logout'), ['action' => 'logout'], ['class' => 'button float-right']) ?>

TIPS: Use password_verify to verifies the password matches a hash. 

<?php
// Your password hash value
$hash = '$2y$10$MC84b2abTpj3TgHbpcTh2OYW5sb2j7YHg.Rj/DWiUBKYRJ5./NaRi';

if (password_verify('123456', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>


Happy coding :)