About the implementation of Remember Me using AuthenticationPlugin's Cookie Authenticator

680 views Asked by At

I use CakePHP's AuthenticationPlugin. I was trying to implement RememberMe functionality into this. I found the following article when I was reading the Cakephp documentation. Cookie Authenticator aka “Remember Me”

However, the documentation here is difficult for me to understand. I have no idea what to do with it. I've successfully implemented EncryptedCookieMiddleware. I have no idea what to do after that. I don't know how to use rememberMeField, how to use fields and how to use cookies.

$this->Authentication->rememberMeField

$this->Authentication->fields

I tried to see if I could use it like these, but it was still no good. Please let me know how to use these. Also, do you know of any RememberMe tutorials? How do I implement it?

Sorry. Please help me...


// in config/app.php
'Security' => [
        .....
        'cookieKey' => env('SECURITY_COOKIE_KEY', 'AnyString'),  // <- add
    ],
// in src/Application.php
use Cake\Http\Middleware\EncryptedCookieMiddleware; // <- add

  // in middleware()
  public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
    {
      $cookies = new EncryptedCookieMiddleware(  // <- add
            ['mail', 'password'],
            Configure::read('Security.cookieKey')
        );

      $middlewareQueue
        // . . .
        ->add($cookies) // <-add
        ->add(new AuthenticationMiddleware($this));

So far I've been able to implement it myself. I'm confident. The problem is after this. We have no idea what to do with it...

A Remember me checkbox was implemented in the template Form. $this->request->getData('rememberMe'); to get it. If this is 1, the checkbox was pressed.

// in src/Controller/UsersController
public function login()
    {
        $this->request->allowMethod(['get', 'post']);

        if ($this->request->is('post')) {
            $result = $this->Authentication->getResult();
            //  If the user is logged in, whether POST or GET, we will redirect
            $requestGetData = $this->request->getData('rememberMe');
            if ($requestGetData['rememberMe'] == 1){
                 $this->Authentication->cookie['name'] = $requestGetData['mail'];
                 $this->Authentication->cookie['name'] = $requestGetData['password']
            }
            if ($result->isValid()) {
                $redirect = $this->request->getQuery('redirect', [
                    'controller' => 'Stores',
                    'action'     => 'index',
                ]);

                return $this->redirect($redirect);
            }
            // If the user fails to authenticate after submitting, an error is displayed.
            if (!$result->isValid()) {
                $this->Flash->error(__('Your email address or password is incorrect.'));
            }
        }
        $title = $this->config('Users.title.login');
        $message = $this->config('Users.message.login');
        $this->set(compact('login_now', 'title', 'message'));
    }

I know that's not true. But I tried to implement something like this just in case. Please help me!


Changed around the login.

public function login()
    {
        $this->request->allowMethod(['get', 'post']);

        if ($this->request->is('post')) {
            $result      = $this->Authentication->getResult();
            $requestData = $this->request->getData();

            if ($result->isValid()) {
                $redirect = $this->request->getQuery('redirect', [
                    'controller' => 'Stores',
                    'action' => 'index',
                ]);
                $this->Authentication->getAuthenticationService()->loadAuthenticator( 'Authentication.Cookie', [
                        'fields' => ['mail', 'password']
                ]
                );
                return $this->redirect($redirect);
            }
            if ($this->request->is('post') && !$result->isValid()) {
                $this->Flash->error(__('Your email address or password is incorrect.'));
            }
        }
        $title = $this->config('Users.title.login');
        $message = $this->config('Users.message.login');
        $this->set(compact('title', 'message'));
    }
1

There are 1 answers

1
ndm On BEST ANSWER

You're not supposed to load authenticators in your controllers, authentication happens at middleware level, before any of your controllers are being invoked.

The cookie authenticator is ment to be loaded and configured just like any other authenticator, that is where you create the authentication service, usually in Application::getAuthenticationService() in src/Application.php.

By default the field in the form must be remember_me, not rememberMe, that is unless you would configure the cookie authenticator's rememberMeField option otherwise.

Furthermore the default cookie name of the cookie authenticator is CookieAuth, so if you wanted to encrypt it, you'd have to use that name in the EncryptedCookieMiddleware config accordingly.

tl;dr

Remove all cookie related code from your controller, and load the authenticator in your Application::getAuthenticationService() method:

use Authentication\Identifier\IdentifierInterface;

// ...

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    $service = new AuthenticationService();

    // ...

    // The cookie authenticator should be loaded _after_ the session authenticator,
    // and _before_ other authenticators like the form authenticator
    $service->loadAuthenticator('Authentication.Cookie', [
        // 'rememberMeField' => 'custom_form_field_name', // if you want to change the default
        'fields' => [
            IdentifierInterface::CREDENTIAL_USERNAME => 'mail',
            IdentifierInterface::CREDENTIAL_PASSWORD => 'password',
        ],
    ]);

    // ...

    return $service;
}

set the authentication cookie name in the EncryptedCookieMiddleware config:

$cookies = new EncryptedCookieMiddleware(
    ['CookieAuth'],
    Configure::read('Security.cookieKey')
);

and change the field name in your form to remember_me if you're using the cookie authenticator's defaults:

echo $this->Form->control('remember_me', [
    'type' => 'checkbox'
]);

That's all that should be required, if you tick the checkbox in your login form, then the authentication middleware will set a cookie after successful authentication accordingly, and it will pick up the cookie if it's present on a request and no other authenticator successfully authenticates the request first (like the session authenticator for example).