Passed
Branch master (372b2a)
by Filipe
01:32
created

AbstractRememberMeHandler::createCookie()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 11
rs 9.9666
cc 2
nc 1
nop 1
1
<?php
2
3
/**
4
 * This file is part of web-stack
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Slick\WebStack\Domain\Security\Http\RememberMe;
13
14
use Slick\WebStack\Domain\Security\Exception\AuthenticationException;
15
use Slick\WebStack\Domain\Security\SecurityException;
16
use Slick\WebStack\Domain\Security\User\UserProviderInterface;
17
use Slick\WebStack\Domain\Security\UserInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Log\LoggerInterface;
20
21
/**
22
 * AbstractRememberMeHandler
23
 *
24
 * @package Slick\WebStack\Domain\Security\Http\RememberMe
25
 */
26
abstract class AbstractRememberMeHandler implements RememberMeHandlerInterface
27
{
28
    /** @var array<string, mixed>  */
29
    protected array $options = [];
30
31
    /**
32
     * Creates a AbstractRememberMeHandler
33
     *
34
     * @param UserProviderInterface $userProvider
35
     * @param ServerRequestInterface $request
36
     * @param array $options
37
     * @param LoggerInterface|null $logger
38
     *
39
     * @phpstan-template U of UserInterface
40
     * @phpstan-param array<string, mixed> $options
41
     * @phpstan-param UserProviderInterface<U> $userProvider
42
     */
43
    public function __construct(
44
        protected readonly UserProviderInterface $userProvider,
45
        protected readonly ServerRequestInterface $request,
46
        array $options = [],
47
        protected readonly ?LoggerInterface $logger = null
48
    ) {
49
        $this->options = $options + [
50
            'cookieName' => 'REMEMBERME',
51
            'lifetime' => 31536000,
52
            'path' => '/',
53
            'domain' => null,
54
            'secure' => false,
55
            'httponly' => true,
56
            'same_site' => null,
57
            'always_remember_me' => false,
58
            'remember_me_parameter' => '_remember_me',
59
        ];
60
    }
61
62
    /**
63
     * Checks if the RememberMeDetails is a valid cookie to log in the given User.
64
     *
65
     * This method should also:
66
     * - Create a new remember-me cookie to be sent with the response;
67
     * - If you store the token somewhere else (e.g. in a database), invalidate the stored token.
68
     *
69
     * @throws AuthenticationException If the remember-me details are not accepted
70
     */
71
    abstract protected function processRememberMe(
72
        RememberMeDetails $rememberMeDetails,
73
        UserInterface $user
74
    ): void;
75
76
    /**
77
     * Creates a remember me cookie.
78
     *
79
     * @param RememberMeDetails|null $rememberMeDetails The details for the cookie, or null to
80
     *                                                  clear the remember-me cookie.
81
     *
82
     * @return void
83
     */
84
    protected function createCookie(?RememberMeDetails $rememberMeDetails): void
85
    {
86
        $scheme = $this->request->getUri()->getScheme();
87
        setcookie(
88
            name: $this->options['cookieName'],
89
            value: $rememberMeDetails ? (string) $rememberMeDetails : '',
90
            expires_or_options: $rememberMeDetails?->expires() ?? 1,
91
            path: $this->options['path'] ?? "",
92
            domain: $this->options['domain'] ?? "",
93
            secure: $this->options['secure'] ?? $scheme == 'https',
94
            httponly: $this->options['httponly']
95
        );
96
    }
97
98
    /**
99
     * @inheritDoc
100
     * @throws SecurityException
101
     */
102
    public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface
103
    {
104
        $user = $this->userProvider->loadUserByIdentifier($rememberMeDetails->userIdentifier());
105
        $this->processRememberMe($rememberMeDetails, $user);
106
107
        $this->logger?->info('Remember-me cookie accepted.');
108
109
        return $user;
110
    }
111
112
    /**
113
     * @inheritDoc
114
     */
115
    public function clearRememberMeCookie(): void
116
    {
117
        $this->logger?->debug('Clearing remember-me cookie.', ['cookieName' => $this->options['cookieName']]);
118
        $this->createCookie(null);
119
    }
120
}
121