I'm writing a web app which I want to have flexible authentication options due to different customers' needs. I'm using the official cakephp/authentication library, but it doesn't have an OpenID Connect Authenticator, so I'm rolling my own.
The problem I'm having is that I can't return a modified Cake\Http\Response object with a redirect header from my Authenticator. AuthenticatorInterface requires the authenticate() method to return a Result object. I'm hoping to not have to modify more than the Authenticator class, because it kind of defeats the purpose of using the library if I'm just going to rewrite it's flow.
class OidcAuthenticator extends AbstractAuthenticator
{
public function authenticate(ServerRequestInterface $request, ResponseInterface $response)
{
// non-pertinent code removed for brevity
return $this->_requestAuthorization($request, $response);
}
private function _requestAuthorization(ServerRequestInterface $request, ResponseInterface $response)
{
$auth_endpoint = $this->getConfig('authorization_endpoint');
$response_type = 'code';
$state = $this->_setState($request, Security::randomString());
$auth_params = [
'response_type' => $response_type,
'redirect_uri' => 'http://localhost:8080/',
'client_id' => $this->getConfig('client_id'),
'nonce' => $nonce,
'state' => $state,
'scope' => 'openid'
];
$auth_endpoint .= (strpos($auth_endpoint, '?') === false ? '?' : '&') .
http_build_query($auth_params, null, '&');
/* What I want to return */
$response = $response->withHeader('Location', $auth_endpoint);
/* What I have to return */
return new Result(null, Result::FAILURE_MISSING_CREDENTIALS);
}
}
I can make this work with
$request->getAttribute('session')->close();
header('Location: ' . $auth_endpoint, true, 302);
exit();
but that seems to violate CakePHP conventions. Ideally, I could embed the new response object into the Result and the Authentication middleware would catch it and emit it instead of the UnauthenticatedException. This is kind of a best-practices question, but hopefully it's specific enough for SO.
TL;DR: How to redirect when you can't return a response object to the middleware queue?