jumbojett / OpenID-Connect-PHP

Minimalist OpenID Connect client

Home Page:https://github.com/jumbojett/OpenID-Connect-PHP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Allow using another session driver to store nonces

nicolus opened this issue · comments

commented

There's a comment at the top of the class saying Please note this class stores nonces by default in $_SESSION['openid_connect_nonce']. This is indeed noteworthy because in some situations it is not desirable : In my case I have a Laravel project hosted on multiple servers beind a load balancer, where sessions are stored in Redis by Laravel's session driver... But of course since this library uses the default PHP sessions (which I had not configured to use Redis), it doesn't work well with load balancing. Even without this, I think if your framework or project already has its own session logic, it should be reused by the OpenIDConnectClient class.

What I ended up doing is Extending the class to override the session methods. If anyone needs something that works with Laravel here's what it looks like :

class OpenIdConnectClientForLaravel extends \Jumbojett\OpenIDConnectClient
{
    protected function startSession() {
        //already handled by Laravel
        return null;
    }

    protected function commitSession() {
        session()->save();
    }

    protected function getSessionKey($key) {
        return request()->session()->get($key);
    }

    protected function setSessionKey($key, $value) {
        request()->session()->put($key, $value);
    }

    protected function unsetSessionKey($key) {
        request()->session()->remove($key);
    }
}

It works well enough, but I don't think overriding protected methods is good practice... If the methods change in the original lib my workaround will stop working.

So I think a better solution would be to extract those 5 methods into a separate class (let's say DefaultSessionDriver that implements a SessionDriverInterface), and then use dependency injection to send this driver to OpenIDConnectClient. The constructor signature would change to something like this :

public function __construct($provider_url = null, $client_id = null, $client_secret = null, $issuer = null, $session_driver = null) {
    if ($session_driver === null) {
        $this->setSessionDriver(new DefaultSessionDriver())
    } else {
        $this->setSessionDriver($session_driver)
    }
}

So that we can either use the default (current) session driver, or pass our own implementation of SessionDriverInterface. Then in the code we'd just need to replace $this->setSessionKey() with $this->session->setKey() and so on...

I can make a PR for this, I just want to know if there's some interest in it, and if the maintainers are OK with adding a new class and a new interface to the project.

Thanks !

It works well enough, but I don't think overriding protected methods is good practice... If the methods change in the original lib my workaround will stop working.

This is the reason why protected methods exists - to allow overriding them.

Your approach is the recommended way and it is done like that in many places.

We also use this concept to change the behavior in many other scenarios as well.

Nice idea!

We are using the following for Laravel:

class OpenIDConnectClient extends BaseOpenIDConnectClient
{

    protected function startSession(): void
    {
        // Laravel magic in the background :)
    }

    protected function commitSession(): void
    {
        Session::save();
    }

    /**
     * @param string $key
     */
    protected function getSessionKey($key): mixed
    {
        if (!Session::has($key)) {
            return false;
        }

        return Session::get($key);
    }

    /**
     * @param string $key
     * @param mixed $value mixed
     */
    protected function setSessionKey($key, $value): void
    {
        Session::put($key, $value);
    }

    /**
     * @param string $key
     */
    protected function unsetSessionKey($key): void
    {
        Session::remove($key);
    }

    /**
     * Overwrite the redirect method to use Laravel's abort method.
     * Sometimes the error 'Cannot modify header information - headers already sent' was thrown.
     * By using Laravel's abort method, this error is prevented.
     * @param string $url
     * @return void
     */
    public function redirect($url): void
    {
        App::abort(302, '', ['Location' => $url]);
    }
}
commented

This is the reason why protected methods exists - to allow overriding them.

Fair enough, I'll stick to what we have then.

Thanks !

@ricklambrechts : I'll steal some of that, it's a bit better than my version ;-)