Passed
Push — master ( c0d33c...41aaf7 )
by Arnold
15:34 queued 05:37
created

CookieMiddleware::getSetCookieHeader()   A

Complexity

Conditions 6
Paths 18

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
dl 0
loc 21
rs 9.2222
c 1
b 0
f 0
cc 6
nc 18
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jasny\Auth\Session\Jwt;
6
7
use Psr\Http\Message\ResponseInterface as Response;
8
use Psr\Http\Message\ServerRequestInterface as ServerRequest;
9
use Psr\Http\Server\MiddlewareInterface;
10
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
11
12
/**
13
 * Middleware to get JWT cookie from a PSR-7 request and set JWT cookie in the response.
14
 */
15
class CookieMiddleware implements MiddlewareInterface
16
{
17
    protected string $name;
18
19
    /**
20
     * @var array<string,mixed>
21
     */
22
    protected array $options = [
23
        'path' => '',
24
        'domain' => '',
25
        'secure' => false,
26
        'httponly' => true,
27
        'samesite' => '',
28
    ];
29
30
    /**
31
     * Cookies constructor.
32
     *
33
     * @param string              $name
34
     * @param array<string,mixed> $options
35
     */
36
    public function __construct(string $name = 'jwt', array $options = [])
37
    {
38
        $this->name = $name;
39
        $this->options = array_change_key_case($options, CASE_LOWER) + $this->options;
40
    }
41
42
    /**
43
     * @inheritDoc
44
     */
45
    public function process(ServerRequest $request, RequestHandler $handler): Response
46
    {
47
        return $this->processRequest($request, [$handler, 'handle']);
48
    }
49
50
    /**
51
     * Get a callback that can be used as double pass middleware.
52
     *
53
     * @return callable
54
     */
55
    public function asDoublePass(): callable
56
    {
57
        return function (ServerRequest $request, Response $response, callable $next): Response {
58
            return $this->processRequest($request, fn(ServerRequest $request) => $next($request, $response));
59
        };
60
    }
61
62
    /**
63
     * Process the PSR-7 request and set the JWT cookie for the response.
64
     *
65
     * @param ServerRequest                    $request
66
     * @param callable(ServerRequest):Response $handle
67
     * @return Response
68
     */
69
    protected function processRequest(ServerRequest $request, callable $handle): Response
70
    {
71
        $cookieValue = $request->getCookieParams()[$this->name] ?? null;
72
        $cookie = new CookieValue($cookieValue);
73
74
        /** @var Response $response */
75
        $response = $handle($request->withAttribute('jwt_cookie', $cookie));
76
77
        // Cookie value has changed => jwt is set (or cleared)
78
        if ($cookie->get() !== $cookieValue) {
79
            $header = $this->getSetCookieHeader($cookie->get() ?? '', $cookie->getExpire());
80
            $response = $response->withAddedHeader('Set-Cookie', $header);
81
        }
82
83
        return $response;
84
    }
85
86
    /**
87
     * Get the header string for `Set-Cookie`.
88
     */
89
    protected function getSetCookieHeader(string $value, int $expire): string
90
    {
91
        $header = $this->name . '=' . urlencode($value);
92
93
        if ($expire !== 0) {
94
            $header .= '; Expires=' . gmdate('D, d M Y H:i:s T', $expire);
95
        }
96
97
        foreach (['Path', 'Domain', 'SameSite'] as $opt) {
98
            $header .= (string)$this->options[strtolower($opt)] !== ''
99
                ? "; {$opt}=" . $this->options[strtolower($opt)]
100
                : '';
101
        }
102
103
        foreach (['Secure', 'HttpOnly'] as $opt) {
104
            $header .= (string)$this->options[strtolower($opt)] !== ''
105
                ? "; {$opt}"
106
                : '';
107
        }
108
109
        return $header;
110
    }
111
}
112