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 on 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 //add this
{
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' => \Cake\Routing\Router::url([
'controller' => 'Users',
'action' => 'login',
'plugin' => null,
'prefix' => null
]),
'queryParam' => 'redirect',
]);
// Load identifiers, ensure we check email and password fields
$authenticationService->loadIdentifier('Authentication.Password', [
'fields' => [
'username' => 'username', //multifield: ['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' => 'username',
'password' => 'password',
],
'loginUrl' => \Cake\Routing\Router::url([
'controller' => 'Users',
'action' => 'login',
'plugin' => null,
'prefix' => null
]),
]);
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 on the 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 verify 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.';
}
?>
View auth info helper:
public function initialize(): void
{
$this->loadHelper('Authentication.Identity');
}
If you want to print the current authenticated username, use the following code:
<?php echo $this->Identity->get('username'); ?>
Logged in simple conditional check:
<?php
if ($this->Identity->isLoggedIn()) {
echo 'User Logged in';
}
?>
To capture currently authenticated user ID in the controller, simply use the following code before save in the controller:
$article->user_id = $this->Authentication->getIdentity('id')->getIdentifier('id'); //capture auth id
That's all, happy coding :)