middlewares / php-session

PSR-15 middleware to create a php session using the request data
MIT License
14 stars 3 forks source link

Middleware does not set session cookie in response #4

Closed compwright closed 3 years ago

compwright commented 3 years ago

By itself, this middleware is incomplete, as it fails to set a session cookie in the PSR-7 response whenever the session ID changes.

For my own use, I have extended this as follows:

<?php

use Middlewares\PhpSession;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class PhpSessionMiddleware extends PhpSession
{
    /**
     * @var string|null
     */
    protected $name;

    /**
     * @var string|null
     */
    protected $id;

    public function __construct()
    {
        if (ini_get('session.use_trans_sid') != false) {
            throw new RuntimeException('session.use_trans_sid must be false');
        }

        if (ini_get('session.use_cookies') != false) {
            throw new RuntimeException('session.use_cookies must be false');
        }

        if (ini_get('session.use_only_cookies') != true) {
            throw new RuntimeException('session.use_only_cookies must be true');
        }

        if (ini_get('session.cache_limiter') !== '') {
            throw new RuntimeException('session.cache_limiter must be an empty string');
        }
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // Preemptively read and inject the session name and ID. This saves us from
        // re-implementing a bunch of private methods from the parent class, and
        // we need the starting session ID to allow us to detect when to write a
        // session cookie since the parent class doesn't handle doing that
        $this->name = $this->name ?: session_name();
        if ($this->name) {
            $this->id = $this->id ?: self::readSessionCookie($request, $this->name);
        }

        $response = parent::process($request, $handler);

        if (session_id() !== $this->id) {
            $response = self::writeSessionCookie(
                $response,
                session_name(),
                session_id(),
                time(),
                session_get_cookie_params()
            );
        }

        return $response;
    }

    private static function readSessionCookie(ServerRequestInterface $request, string $name): string
    {
        $cookies = $request->getCookieParams();
        return $cookies[$name] ?? '';
    }

    private static function writeSessionCookie(ResponseInterface $response, string $name, string $id, int $now, array $params): ResponseInterface
    {
        $cookie = urlencode($name) . '=' . urlencode($id);

        if ($params['lifetime']) {
            $expires = gmdate('D, d M Y H:i:s T', $now + $params['lifetime']);
            $cookie .= "; expires={$expires}; max-age={$params['lifetime']}";
        }

        if ($params['domain']) {
            $cookie .= "; domain={$params['domain']}";
        }

        if ($params['path']) {
            $cookie .= "; path={$params['path']}";
        }

        if ($params['secure']) {
            $cookie .= '; secure';
        }

        if ($params['httponly']) {
            $cookie .= '; httponly';
        }

        return $response->withAddedHeader('Set-Cookie', $cookie);
    }
}

It would be nice to see this functionality incorporated.

oscarotero commented 3 years ago

Yes, sure! Do you want to work on a pull request?