cakephp / cakephp/authentication

Next major: add preserveImpersonation option to setIdentity() / clearIdentity()

Open
#797 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
PHP
Stars
118
Forks
104
Avg merge
1d 16h
Merged PRs (30d)
3

Description

Background

PR #788 added AuthenticationComponent::replaceIdentity() for the common request-only identity refresh case (e.g. attaching eager-loaded associations in beforeFilter() without ending an active impersonation).

An earlier iteration of that PR also expanded two existing signatures:

  • AuthenticationComponent::setIdentity(ArrayAccess|array $identity, bool $preserveImpersonation = false)
  • AuthenticationService::clearIdentity(ServerRequestInterface $request, ResponseInterface $response, bool $stopImpersonation = true)

Those were reverted before merge to keep the public API surface unchanged on the 4.x minor. The signature growth belongs in the next major, where adding the flag to PersistenceInterface::clearIdentity() itself is clean.

Proposal for the next major

Add a first-class "persist a refreshed identity while keeping impersonation alive" path. Unlike replaceIdentity() (request attribute only, lost on the next request), this survives into subsequent requests.

1. Extend the interface
// PersistenceInterface
public function clearIdentity(
    ServerRequestInterface $request,
    ResponseInterface $response,
    bool $stopImpersonation = true,
): array;
2. Honor the flag in AuthenticationService::clearIdentity()
public function clearIdentity(
    ServerRequestInterface $request,
    ResponseInterface $response,
    bool $stopImpersonation = true,
): array {
    foreach ($this->authenticators() as $authenticator) {
        if ($authenticator instanceof PersistenceInterface) {
            if (
                $stopImpersonation
                && $authenticator instanceof ImpersonationInterface
                && $authenticator->isImpersonating($request)
            ) {
                $stopImpersonationResult = $authenticator->stopImpersonating($request, $response);
                ['request' => $request, 'response' => $response] = $stopImpersonationResult;
            }
            $result = $authenticator->clearIdentity($request, $response);
            ['request' => $request, 'response' => $response] = $result;
        }
    }
    if ($stopImpersonation) {
        $this->_successfulAuthenticator = null;
    }

    return [
        'request' => $request->withoutAttribute($this->getConfig('identityAttribute')),
        'response' => $response,
    ];
}
3. Add the opt-in to the component
public function setIdentity(ArrayAccess|array $identity, bool $preserveImpersonation = false)
{
    $controller = $this->getController();
    $service = $this->getAuthenticationService();

    $service->clearIdentity(
        $controller->getRequest(),
        $controller->getResponse(),
        stopImpersonation: !$preserveImpersonation,
    );

    /** @var array{request: \Cake\Http\ServerRequest, response: \Cake\Http\Response} $result */
    $result = $service->persistIdentity(
        $controller->getRequest(),
        $controller->getResponse(),
        $identity,
    );

    $controller->setRequest($result['request']);
    $controller->setResponse($result['response']);

    return $this;
}
Usage
// Refresh the impersonated user and keep it across requests, without
// reverting to the impersonator:
$this->Authentication->setIdentity($reloaded, preserveImpersonation: true);

Note this also skips the session rotation that the default setIdentity() flow performs - it is a refresh, not a privilege transition, so it must not be used for login or role changes. That caveat should be documented.

Notes
  • Because clearIdentity() is on PersistenceInterface, adding the parameter there is a hard BC break and is why this is deferred to the major.
  • Tests for both the preserveImpersonation: true persist case and the default-ends-impersonation case existed in the PR branch history and can be lifted from there.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with PersistenceInterface, AuthenticationService::clearIdentity(), and AuthenticationComponent::setIdentity(), then inspect the existing tests and PR #788 branch history. Done means the preserveImpersonation path persists refreshed identities across requests, the default path still ends impersonation, and the session-rotation caveat is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
authentication, backend-api-design
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.