CakePHP4.x SAML Authentication cannot create session

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);
    }
}

In order to persist your identity to the session, your authenticator needs to implement the PersistenceInterface.

1 Like

Did you eventually solve your issues with getting SAML and the CakePHP Authentication plugin working together? We are about to start implementing and I was wondering if you mind sharing some of your experiences, issues and sollutions with me? Also, is there a specific reason you chose for Onelogin as opposed to SimpleSAMLphp?