ARTICLE AD BOX
setIdentity() is the cause. In Authentication 3.x it clears the current identity first, then persists the new one. That clear step stops impersonation and also resets the successful authenticator/provider, so the following impersonate() call has no provider to use and throws No AuthenticationProvider present. The API docs explicitly describe setIdentity() as clearing and replacing identity data, and the source clears impersonation before clearing the session identity.
// Save new profile data first $user = $this->Users->saveOrFail($user); // If currently impersonating, DO NOT call setIdentity() if ($this->Authentication->isImpersonating()) { $session = $this->request->getSession(); // Default key for Authentication.Session authenticator is "Auth". // Change this if you configured another sessionKey. $session->write('Auth', $user); // Usually redirect after this so the next request rebuilds the identity. return $this->redirect($this->referer()); } // Normal non-impersonated user $this->Authentication->setIdentity($user); return $this->redirect($this->referer());So the fix is: when impersonating, refresh the impersonated user stored in the session instead of calling setIdentity().
If you need the updated identity during the same request too, also update the request attribute:
use Authentication\Identity; use Authentication\IdentityInterface; // Save new profile data first $user = $this->Users->saveOrFail($user); if ($this->Authentication->isImpersonating()) { $session = $this->request->getSession(); // For Authentication.Session $session->write('Auth', $user); // Update current request identity as well $identity = $user instanceof IdentityInterface ? $user : new Identity($user); $identityAttribute = $this->Authentication ->getAuthenticationService() ->getIdentityAttribute(); $this->setRequest( $this->getRequest()->withAttribute($identityAttribute, $identity) ); } else { $this->Authentication->setIdentity($user); }Do not do this:
$this->Authentication->setIdentity($user); $this->Authentication->impersonate($user2)After setIdentity(), the Authentication service no longer has a successful authentication provider, and impersonate() depends on that provider. That is why you get No AuthenticationProvider present.
Best long-term fix: use Authentication.PrimaryKeySession instead of storing the whole user entity in the session. CakePHP’s Authentication docs say PrimaryKeySession stores only the primary key and fetches fresh data from the database, so “issues like having to update the identity when updating account data should be gone.”
$service->loadAuthenticator('Authentication.PrimaryKeySession', [ 'identifier' => [ 'Authentication.Token' => [ 'tokenField' => 'id', 'dataField' => 'key', 'resolver' => 'Authentication.Orm', ], ], ]); $service->loadAuthenticator('Authentication.Form', [ 'identifier' => 'Authentication.Password', 'fields' => [ 'username' => 'email', 'password' => 'password', ], ]);With PrimaryKeySession, after saving profile changes, you usually do not need setIdentity() at all; the next request will load fresh user data from the database while keeping impersonation active.
