JWTAuthListener   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 88
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 7
c 1
b 0
f 1
lcom 1
cbo 8
dl 0
loc 88
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B handle() 0 59 6
1
<?php
2
3
namespace ApiBundle\Security\Firewall;
4
5
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
6
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
7
use Symfony\Component\HttpKernel\Exception\HttpException;
8
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpFoundation\JsonResponse;
11
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
12
use ApiBundle\Security\Authentication\Token\JWTUserToken;
13
14
class JWTAuthListener implements ListenerInterface
15
{
16
    private $providerKey;
17
18
    private $privkey;
19
20
    private $passphrase;
21
22
    /**
23
     * @var AuthenticationManagerInterface
24
     */
25
    private $authenticationManager;
26
27
    /**
28
     * @param AuthenticationManagerInterface $authenticationManager
29
     * @param $providerKey
30
     */
31
    public function __construct(AuthenticationManagerInterface $authenticationManager, $providerKey, array $config)
32
    {
33
        $this->authenticationManager = $authenticationManager;
34
        $this->providerKey = $providerKey;
35
        $this->privkey = 'file://' . realpath($config['priv_key']);
36
        $this->passphrase = $config['passphrase'];
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function handle(GetResponseEvent $event)
43
    {
44
        $request = $event->getRequest();
45
        if (!$request->isMethod('POST')) {
46
            throw new HttpException(405, "Only POST method is allowed for JWT authentication");
47
        }
48
49
        $username = $request->request->get('username', null);
50
        $password = $request->request->get('password', null);
51
52
        try {
53
            $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
54
        } catch (\InvalidArgumentException $e) {
55
            // most probably failed to find user by these credentials
56
            // let other unexpected exceptions pass through
57
            throw new HttpException(JsonResponse::HTTP_UNAUTHORIZED, "Username or password is not valid.", $e);
58
        }
59
60
        $user = $token->getUser();
61
62
        $header = [];
63
64
        // jwt token data
65
        $payload = [
66
            'username' => $user->getUsername(),
67
            'exp' => (new \DateTime('+1 day'))->format('U'),
68
            'iat' => (new \DateTime('now'))->format('U'),
69
        ];
70
71
        // build jwt data to sign
72
        $toSign = implode('.', array_map('base64_encode', array_map('json_encode', [$header, $payload])));
73
74
        // init openssl private key resource
75
        $key = openssl_pkey_get_private($this->privkey, $this->passphrase);
76
        if (!is_resource($key)) {
77
            throw new HttpException(500, "not valid private key, {$this->privkey}");
78
        }
79
80
        // ensure key is valid RSA private key
81
        if (openssl_pkey_get_details($key)['type'] !== JWTUserToken::KEY_TYPE) {
82
            throw new HttpException(500, "Only RSA keys are supported.");
83
        }
84
85
        // create signature
86
        $signature = null;
87
        if (!openssl_sign($toSign, $signature, $key, JWTUserToken::ALGO)) {
88
            throw new HttpException(500, "could not sign JWT.");
89
        }
90
91
        // create jwt token
92
        $jwt = implode('.', [$toSign, base64_encode($signature)]);
93
94
        // finally create response
95
        $event->setResponse(new JsonResponse([
96
            'token' => $jwt,
97
            'id' => $user->getId(),
98
            'roles' => $user->getRoles(),
99
        ]));
100
    }
101
}
102