JWTCreatedSubscriber   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 100
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 26
c 1
b 0
f 0
dl 0
loc 100
ccs 35
cts 35
cp 1
rs 10
wmc 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A onJWTCreated() 0 16 1
A getSubscribedEvents() 0 5 1
A setExpiration() 0 4 1
A setSecurityData() 0 19 2
A setLocalizationData() 0 13 4
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/EventSubscriber/JWTCreatedSubscriber.php
5
 *
6
 * @author TLe, Tarmo Leppänen <[email protected]>
7
 */
8
9
namespace App\EventSubscriber;
10
11
use App\Enum\Language;
12
use App\Enum\Locale;
13
use App\Security\SecurityUser;
14
use App\Service\Localization;
0 ignored issues
show
Bug introduced by
The type App\Service\Localization was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use DateTime;
16
use DateTimeZone;
17
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
18
use Lexik\Bundle\JWTAuthenticationBundle\Events;
19
use Psr\Log\LoggerInterface;
20
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
21
use Symfony\Component\HttpFoundation\RequestStack;
22
use Symfony\Component\Security\Core\User\UserInterface;
23
use function hash;
24
use function implode;
25
26
/**
27
 * Class JWTCreatedSubscriber
28
 *
29
 * @package App\EventSubscriber
30
 * @author TLe, Tarmo Leppänen <[email protected]>
31
 */
32
class JWTCreatedSubscriber implements EventSubscriberInterface
33
{
34 20
    public function __construct(
35
        private readonly RequestStack $requestStack,
36
        private readonly LoggerInterface $logger,
37
    ) {
38 20
    }
39
40
    /**
41
     * {@inheritdoc}
42
     *
43
     * @return array<string, string>
44
     */
45 1
    public static function getSubscribedEvents(): array
46
    {
47 1
        return [
48 1
            JWTCreatedEvent::class => 'onJWTCreated',
49 1
            Events::JWT_CREATED => 'onJWTCreated',
50 1
        ];
51
    }
52
53
    /**
54
     * Subscriber method to attach some custom data to current JWT payload.
55
     *
56
     * This method is called when following event is broadcast;
57
     *  - lexik_jwt_authentication.on_jwt_created
58
     */
59 20
    public function onJWTCreated(JWTCreatedEvent $event): void
60
    {
61
        // Get current original payload
62 20
        $payload = $event->getData();
63
64
        // Set localization data
65 20
        $this->setLocalizationData($payload, $event->getUser());
66
67
        // Update JWT expiration data
68 20
        $this->setExpiration($payload);
69
70
        // Add some extra security data to payload
71 20
        $this->setSecurityData($payload);
72
73
        // And set new payload for JWT
74 20
        $event->setData($payload);
75
    }
76
77
    /**
78
     * @param array<string, string> $payload
79
     */
80 20
    private function setLocalizationData(array &$payload, UserInterface $user): void
81
    {
82 20
        $payload['language'] = $user instanceof SecurityUser
83 19
            ? $user->getLanguage()->value
84 1
            : Language::getDefault()->value;
85
86 20
        $payload['locale'] = $user instanceof SecurityUser
87 19
            ? $user->getLocale()->value
88 1
            : Locale::getDefault()->value;
89
90 20
        $payload['timezone'] = $user instanceof SecurityUser
91 19
            ? $user->getTimezone()
92 1
            : Localization::DEFAULT_TIMEZONE;
93
    }
94
95
    /**
96
     * Method to set/modify JWT expiration date dynamically.
97
     *
98
     * @param array<string, string|int> $payload
99
     */
100 20
    private function setExpiration(array &$payload): void
101
    {
102
        // Set new exp value for JWT
103 20
        $payload['exp'] = (new DateTime('+1 day', new DateTimeZone('UTC')))->getTimestamp();
104
    }
105
106
    /**
107
     * Method to add some security related data to JWT payload, which are checked on JWT decode process.
108
     *
109
     * @see JWTDecodedListener
110
     *
111
     * @param array<string, string|int> $payload
112
     */
113 20
    private function setSecurityData(array &$payload): void
114
    {
115
        // Get current request
116 20
        $request = $this->requestStack->getCurrentRequest();
117
118 20
        if ($request === null) {
119 2
            $this->logger->alert('Request not available');
120
121 2
            return;
122
        }
123
124
        // Get bits for checksum calculation
125 18
        $bits = [
126 18
            $request->getClientIp(),
127 18
            $request->headers->get('User-Agent'),
128 18
        ];
129
130
        // Attach checksum to JWT payload
131 18
        $payload['checksum'] = hash('sha512', implode('|', $bits));
132
    }
133
}
134