Completed
Push — master ( 6d034f...30ccda )
by Tarmo
28s queued 14s
created

JWTCreatedSubscriber::onJWTCreated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

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