JWTDecodedSubscriber   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 67
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 67
ccs 26
cts 26
cp 1
rs 10
c 0
b 0
f 0
wmc 9

4 Methods

Rating   Name   Duplication   Size   Complexity  
A onJWTDecoded() 0 15 3
A __construct() 0 4 1
A getSubscribedEvents() 0 5 1
A checkPayload() 0 20 4
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/EventSubscriber/JWTDecodedSubscriber.php
5
 *
6
 * @author TLe, Tarmo Leppänen <[email protected]>
7
 */
8
9
namespace App\EventSubscriber;
10
11
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;
12
use Lexik\Bundle\JWTAuthenticationBundle\Events;
13
use Psr\Log\LoggerInterface;
14
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\RequestStack;
17
use function array_key_exists;
18
use function hash;
19
use function implode;
20
21
/**
22
 * Class JWTDecodedSubscriber
23
 *
24
 * @package App\EventSubscriber
25
 * @author TLe, Tarmo Leppänen <[email protected]>
26
 */
27
class JWTDecodedSubscriber implements EventSubscriberInterface
28
{
29 256
    public function __construct(
30
        private readonly RequestStack $requestStack,
31
        private readonly LoggerInterface $logger,
32
    ) {
33 256
    }
34
35
    /**
36
     * {@inheritdoc}
37
     *
38
     * @return array<string, string>
39
     */
40 1
    public static function getSubscribedEvents(): array
41
    {
42 1
        return [
43 1
            JWTDecodedEvent::class => 'onJWTDecoded',
44 1
            Events::JWT_DECODED => 'onJWTDecoded',
45 1
        ];
46
    }
47
48
    /**
49
     * Subscriber method to make some custom JWT payload checks.
50
     *
51
     * This method is called when 'lexik_jwt_authentication.on_jwt_decoded' event is broadcast.
52
     */
53 256
    public function onJWTDecoded(JWTDecodedEvent $event): void
54
    {
55
        // No need to continue event is invalid
56 256
        if (!$event->isValid()) {
57 1
            return;
58
        }
59
60 255
        $request = $this->requestStack->getCurrentRequest();
61
62 255
        $this->checkPayload($event, $request);
63
64 255
        if ($request === null) {
65 2
            $this->logger->error('Request not available');
66
67 2
            $event->markAsInvalid();
68
        }
69
    }
70
71
    /**
72
     * Method to check payload data.
73
     */
74 255
    private function checkPayload(JWTDecodedEvent $event, ?Request $request): void
75
    {
76 255
        if ($request === null) {
77 2
            return;
78
        }
79
80 253
        $payload = $event->getPayload();
81
82
        // Get bits for checksum calculation
83 253
        $bits = [
84 253
            $request->getClientIp(),
85 253
            $request->headers->get('User-Agent'),
86 253
        ];
87
88
        // Calculate checksum
89 253
        $checksum = hash('sha512', implode('|', $bits));
90
91
        // Custom checks to validate user's JWT
92 253
        if (!array_key_exists('checksum', $payload) || $payload['checksum'] !== $checksum) {
93 1
            $event->markAsInvalid();
94
        }
95
    }
96
}
97