Completed
Push — master ( 9ab4b9...065cdd )
by Alejandro
09:44
created

JWTService::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 12
ccs 9
cts 9
cp 1
crap 1
rs 9.4285
1
<?php
2
namespace Shlinkio\Shlink\Rest\Authentication;
3
4
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
5
use Firebase\JWT\JWT;
6
use Shlinkio\Shlink\Core\Options\AppOptions;
7
use Shlinkio\Shlink\Rest\Entity\ApiKey;
8
use Shlinkio\Shlink\Rest\Exception\AuthenticationException;
9
10
class JWTService implements JWTServiceInterface
11
{
12
    /**
13
     * @var AppOptions
14
     */
15
    private $appOptions;
16
17
    /**
18
     * JWTService constructor.
19
     * @param AppOptions $appOptions
20
     *
21
     * @Inject({AppOptions::class})
22
     */
23 6
    public function __construct(AppOptions $appOptions)
24
    {
25 6
        $this->appOptions = $appOptions;
26 6
    }
27
28
    /**
29
     * Creates a new JSON web token por provided API key
30
     *
31
     * @param ApiKey $apiKey
32
     * @param int $lifetime
33
     * @return string
34
     */
35 1
    public function create(ApiKey $apiKey, $lifetime = self::DEFAULT_LIFETIME)
36
    {
37 1
        $currentTimestamp = time();
38
39 1
        return $this->encode([
40 1
            'iss' => $this->appOptions->__toString(),
41 1
            'iat' => $currentTimestamp,
42 1
            'exp' => $currentTimestamp + $lifetime,
43 1
            'sub' => 'auth',
44 1
            'key' => $apiKey->getId(), // The ID is opaque. Returning the key would be insecure
45 1
        ]);
46
    }
47
48
    /**
49
     * Refreshes a token and returns it with the new expiration
50
     *
51
     * @param string $jwt
52
     * @param int $lifetime
53
     * @return string
54
     * @throws AuthenticationException If the token has expired
55
     */
56 1
    public function refresh($jwt, $lifetime = self::DEFAULT_LIFETIME)
57
    {
58 1
        $payload = $this->getPayload($jwt);
59 1
        $payload['exp'] = time() + $lifetime;
60 1
        return $this->encode($payload);
61
    }
62
63
    /**
64
     * Verifies that certain JWT is valid
65
     *
66
     * @param string $jwt
67
     * @return bool
68
     */
69 2
    public function verify($jwt)
70
    {
71
        try {
72
            // If no exception is thrown while decoding the token, it is considered valid
73 2
            $this->decode($jwt);
74 1
            return true;
75 1
        } catch (\UnexpectedValueException $e) {
76 1
            return false;
77
        }
78
    }
79
80
    /**
81
     * Decodes certain token and returns the payload
82
     *
83
     * @param string $jwt
84
     * @return array
85
     * @throws AuthenticationException If the token has expired
86
     */
87 3
    public function getPayload($jwt)
88
    {
89
        try {
90 3
            return $this->decode($jwt);
91 1
        } catch (\UnexpectedValueException $e) {
92 1
            throw AuthenticationException::expiredJWT($e);
93
        }
94
    }
95
96
    /**
97
     * @param array $data
98
     * @return string
99
     */
100 2
    protected function encode(array $data)
101
    {
102 2
        return JWT::encode($data, $this->appOptions->getSecretKey(), self::DEFAULT_ENCRYPTION_ALG);
103
    }
104
105
    /**
106
     * @param $jwt
107
     * @return array
108
     */
109 5
    protected function decode($jwt)
110
    {
111 5
        return (array) JWT::decode($jwt, $this->appOptions->getSecretKey(), [self::DEFAULT_ENCRYPTION_ALG]);
112
    }
113
}
114