Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.24% covered (success)
95.24%
20 / 21
83.33% covered (success)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
EntraIdSession
95.24% covered (success)
95.24%
20 / 21
83.33% covered (success)
83.33%
5 / 6
11
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 createCurrentSessionKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCurrentSessionKey
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 setCurrentSessionKey
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 setCookie
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 getCookie
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Session class for Entra ID.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2024-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2024-11-02
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Auth\EntraId;
21
22use Exception;
23use phpMyFAQ\Configuration;
24use phpMyFAQ\Session\AbstractSession;
25use Symfony\Component\HttpFoundation\Cookie;
26use Symfony\Component\HttpFoundation\Request;
27use Symfony\Component\HttpFoundation\Session\Session;
28use Symfony\Component\Uid\Uuid;
29
30class EntraIdSession extends AbstractSession
31{
32    /** @var string EntraID session key */
33    final public const string ENTRA_ID_SESSION_KEY = 'pmf-entra-id-session-key';
34
35    final public const string ENTRA_ID_OAUTH_VERIFIER = 'pmf-entra-id-oauth-verifier';
36
37    final public const string ENTRA_ID_JWT = 'pmf-entra-id-jwt';
38
39    private string $currentSessionKey;
40
41    public function __construct(
42        private readonly Configuration $configuration,
43        private readonly Session $session,
44    ) {
45        parent::__construct($session);
46
47        $this->createCurrentSessionKey();
48    }
49
50    /**
51     * Creates the current UUID session key
52     */
53    public function createCurrentSessionKey(): void
54    {
55        $this->currentSessionKey = Uuid::v4()->toRfc4122();
56    }
57
58    /**
59     * Returns the current UUID session key
60     */
61    public function getCurrentSessionKey(): ?string
62    {
63        $sessionKey = $this->currentSessionKey ?? $this->session->get(self::ENTRA_ID_SESSION_KEY);
64
65        return $sessionKey === null ? null : (string) $sessionKey;
66    }
67
68    /**
69     * Sets the current UUID session key
70     *
71     * @throws Exception
72     */
73    public function setCurrentSessionKey(): EntraIdSession
74    {
75        /* @mago-expect lint:no-isset - typed property may be uninitialized */
76        if (!isset($this->currentSessionKey)) {
77            $this->createCurrentSessionKey();
78        }
79
80        $this->session->set(self::ENTRA_ID_SESSION_KEY, $this->currentSessionKey);
81
82        return $this;
83    }
84
85    /**
86     * Store the Session ID into a persistent cookie expiring
87     * 3600 seconds after the page request.
88     *
89     * @param string          $name Cookie name
90     * @param int|string|null $sessionId Session ID
91     * @param int             $timeout Cookie timeout
92     */
93    public function setCookie(string $name, int|string|null $sessionId, int $timeout = 3600, bool $strict = true): void
94    {
95        $request = Request::createFromGlobals();
96
97        $cookieDomain = parse_url($this->configuration->getDefaultUrl(), PHP_URL_HOST);
98
99        Cookie::create($name)
100            ->withValue($sessionId === null ? '' : (string) $sessionId)
101            ->withExpires((int) $request->server->get('REQUEST_TIME') + $timeout)
102            ->withPath(dirname((string) $request->server->get('SCRIPT_NAME')))
103            ->withDomain(is_string($cookieDomain) ? $cookieDomain : null)
104            ->withSameSite($strict ? 'strict' : '')
105            ->withSecure($request->isSecure())
106            ->withHttpOnly();
107    }
108
109    /**
110     * Returns the value of a cookie.
111     *
112     * @param string $name Cookie name
113     */
114    public function getCookie(string $name): string
115    {
116        $request = Request::createFromGlobals();
117        return $request->cookies->get($name, '');
118    }
119}