Token authentication on CakePHP 4 with plugin

Hi everyone!

I’m developing an app with two authentication models. One is a forma authentication, wich acts properly. The other one is token authentication, because the app will publish an api. All of this made with the Authentication plugin.

So, I created a Tokens table, linked to the Users table vía foreign key (Tokens.user_id). This table has a Token field, with hashing. This is the code:

<?php
declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;
use Authentication\PasswordHasher\DefaultPasswordHasher;
/**
 * Token Entity
 *
 * @property int $id
 * @property int $user_id
 * @property string $name
 * @property string $scope
 * @property \Cake\I18n\FrozenTime $created
 * @property \Cake\I18n\FrozenTime $modified
 * @property string $token
 * @property int $enabled
 */
class Token extends Entity
{
    /**
     * Fields that can be mass assigned using newEntity() or patchEntity().
     *
     * Note that when '*' is set to true, this allows all unspecified fields to
     * be mass assigned. For security purposes, it is advised to set '*' to false
     * (or remove it), and explicitly make individual fields accessible as needed.
     *
     * @var array<string, bool>
     */
    protected $_accessible = [
        'user_id' => true,
        'name' => true,
        'scope' => true,
        'created' => true,
        'modified' => true,
        'token' => true,
        'enabled' => true,
    ];

    /**
     * Fields that are excluded from JSON versions of the entity.
     *
     * @var array<string>
     */
    protected $_hidden = [
        'token',
    ];
    
    protected function _setToken(string $token)
    {
        $hasher = new DefaultPasswordHasher();
        return $hasher->hash($token);
    }
    
    
}

Then, I added this to the Application class:

if ($request->is('json')) {
            $authenticationService = new AuthenticationService([
                'queryParam' => 'redirect',
            ]);


            $authenticationService->loadIdentifier('Authentication.Token', [
                'tokenField' => 'token',
                'dataField' => 'token',
                'resolver' => [
                    'className' => 'Authentication.Orm',
                    'userModel' => 'Tokens',
                    'finder' => 'all',
                ],
            ]);
            
            $authenticationService->loadAuthenticator('Authentication.Token', [
                'queryParam' => 'token',
                'header' => 'Authorization',
                'tokenPrefix' => 'Token',
            ]);
        }

The thing is, this method of authentication works perfectly if I don’t hash the token in the request. I can’t find how to configure the identifier in order to hash the request data correctly. Has anyone any working example of this? I went through the documentation several times.

Saludos,

Edit: Have you looked into the hashAlgorithm option you can pass to the TokenIdentifier?

$authenticationService->loadIdentifier('Authentication.Token', [
    'hashAlgorithm' => '__HASH_ALGORITHM_HERE__',
    'tokenField' => 'token',
    'dataField' => 'token',
    'resolver' => [
        'className' => 'Authentication.Orm',
        'userModel' => 'Tokens',
        'finder' => 'all',
    ],
]);

The TokenIdentifier class that comes with the AuthenticationPlugin is comparing the database value against the value provided in the request, without hashing it.

You can either implement your own TokenIdentifier, by extending CakePHP’s and overwrite the identify method, or you can define your own finder method.

Hi Mentis,

Thanks for your answer. I have observed the existence of the hashAlgorithm option, but I don’t know which value I should write there that corresponds with the default password hasher.

I haven’t found that in any of the doc or source code for the plugin. Do you know the values accepted by that option?

Saludos,