I would like to add Authentication by SAML for CakePHP4.x.
I’m using Authentication plugin.
I’m also using one login’s php library. GitHub - onelogin/php-saml: Simple SAML toolkit for PHP
I added custom Authenticator Class like other similar classes(authentication/authenticators.rst at master · cakephp/authentication · GitHub) and was able to get user info from IdP.
However, though I’m returning user info and “Result::SUCCESS” at the end of authenticate function, Auth information cannot be created in session.
Is there any process I’m missing?
./src/Application.php
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
$service->setConfig([
'unauthenticatedRedirect' => Router::url([
'_host' => getenv('SERVICE_HOST'),
'_full' => true,
'_name' => 'auth:login',
]),
]);
$uri = $request->getUri();
$path = $uri->getPath();
// Load Custom Authenticator class
if ($path === '/saml/acs') {
$service->loadAuthenticator('SAML');
}
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => $loginApiUrl,
]);
return $authenticationService;
}
./src/Authenticator/SAMLAuthenticator.php
class SAMLAuthenticator extends AbstractAuthenticator
{
/**
* @var array
*/
protected $_defaultConfig = [];
private $spBaseUrl;
public function __construct()
{
Utils::setProxyVars(true); //https://github.com/onelogin/php-saml/issues/56
$this->spBaseUrl = env('DEFAULT_PROTOCOL') . $_SERVER['HTTP_HOST'];
}
/**
* @return OneLogin\Saml2\Auth
*/
private function initAuthObject(int $Id)
{
$ssoInfo = $this->Sso->findByTeamId($Id)->first();
$settings = [];
$metadataInfo = $this->setMetadata($ssoInfo);
$settingsInfo = IdPMetadataParser::injectIntoSettings($settings, $metadataInfo);
return new Auth($settingsInfo);
}
private function setMetadata(object $ssoInfo): array
{
$metadataInfo = [
'sp' => [
'entityId' => $this->spBaseUrl . '/saml/metadata',
'assertionConsumerService' => [
'url' => $this->spBaseUrl . '/saml/acs'
],
'NameIDFormat' => $ssoInfo['nameid_format'],
],
'idp' => [
'entityId' => $ssoInfo['entity_id'],
'singleSignOnService' => [
'url' => $ssoInfo['sso_url']
],
'singleLogoutService' => [
'url' => $ssoInfo['slo_url']
],
'x509cert' => $ssoInfo['certificate']
],
];
return $metadataInfo;
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request request
* @return \Authentication\Authenticator\ResultInterface
*/
public function authenticate(ServerRequestInterface $request): ResultInterface
{
$auth = $this->initAuthObject($Id);
if (empty($_REQUEST['SAMLResponse']) || empty($_REQUEST['RelayState'])){
return new Result(null, Result::FAILURE_CREDENTIALS_MISSING);
}
$auth->processResponse();
$errors = $auth->getErrors();
if (!empty($errors)){
$reason = $auth->getLastErrorReason();
Log::info('SAMLAuthenticator::authenticate:' . $reason);
return new Result(null, Result::FAILURE_CREDENTIALS_INVALID);
}
$attributes = $auth->getAttributes();
if (empty($attributes)) {
return new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND);
}
$user = new ArrayObject($attributes);
return new Result($user, Result::SUCCESS);
}
}