Passed
Push — master ( af0ca9...11f3be )
by Sébastien
02:05
created

TokenManager::parseToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
ccs 0
cts 5
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Service;
6
7
use App\Service\Exception\TokenAudienceException;
8
use App\Service\Exception\TokenExpiredException;
9
use App\Service\Exception\TokenIssuerException;
10
use App\Service\Exception\TokenParseException;
11
use App\Service\Exception\TokenSignatureException;
12
use App\Service\Exception\TokenValidationExceptionInterface;
13
use Lcobucci\JWT\Builder;
14
use Lcobucci\JWT\Parser;
15
use Lcobucci\JWT\Signer\BaseSigner;
16
use Lcobucci\JWT\Signer\Hmac\Sha256;
17
use Lcobucci\JWT\Token;
18
use Lcobucci\JWT\ValidationData;
19
use Ramsey\Uuid\Uuid;
20
21
class TokenManager
22
{
23
    public const DEFAULT_EXPIRY = 3600;
24
25
    /**
26
     * @var BaseSigner
27
     */
28
    private $signer;
29
    /**
30
     * @var string
31
     */
32
    private $issuer;
33
    /**
34
     * @var string
35
     */
36
    private $audience;
37
    /**
38
     * @var string
39
     */
40
    private $privateKey;
41
42
    /**
43
     * @var int
44
     */
45
    private $defaultExpiry;
46
47 1
    public function __construct(
48
        string $privateKey,
49
                                int $defaultExpiry = self::DEFAULT_EXPIRY,
50
                                string $issuer = '',
51
                                string $audience = ''
52
    ) {
53 1
        $this->signer        = $this->getSigner();
54 1
        $this->issuer        = $issuer;
55 1
        $this->audience      = $audience;
56 1
        $this->privateKey    = $privateKey;
57 1
        $this->defaultExpiry = $defaultExpiry;
58 1
    }
59
60 1
    public function getDefaultExpiry(): int
61
    {
62 1
        return $this->defaultExpiry;
63
    }
64
65
    public function createNewToken(array $customClaims = [], int $expiration = 3600, bool $autoSign = true): Token
66
    {
67
        $builder = (new Builder())
68
            ->setIssuer($this->issuer) // Configures the issuer (iss claim)
69
            ->setAudience($this->audience) // Configures the audience (aud claim)
70
            ->setId(Uuid::uuid1()->toString(), true) // Configures the id (jti claim), replicating as a header item
71
            ->setIssuedAt(time()) // Configures the time that the token was issue (iat claim)
72
            ->setNotBefore(time() + 0) // Configures the time that the token can be used (nbf claim)
73
            ->setExpiration(time() + $expiration); // Configures the expiration time of the token (exp claim)
74
75
        foreach ($customClaims as $key => $value) {
76
            $builder->set($key, $value);
77
        }
78
79
        if ($autoSign) {
80
            return $this->signToken($builder);
81
        }
82
83
        return $builder->getToken();
84
    }
85
86
    /**
87
     * Sign the token.
88
     */
89
    public function signToken(Builder $builder): Token
90
    {
91
        return $builder->sign($this->signer, $this->privateKey)
92
            ->getToken(); // Retrieves the generated token
93
    }
94
95
    /**
96
     * @throws TokenParseException
97
     */
98
    public function parseToken(string $tokenString): Token
99
    {
100
        $tokenParser = new Parser();
101
        try {
102
            return $tokenParser->parse($tokenString);
103
        } catch (\Throwable $e) {
104
            throw new TokenParseException($e->getMessage());
105
        }
106
    }
107
108
    /**
109
     * Ensure that the token signature is valid and
110
     * the token have not been tampered.
111
     *
112
     * @throws TokenSignatureException
113
     */
114
    public function ensureValidSignature(Token $token): void
115
    {
116
        if (!$this->verifySignature($token)) {
117
            throw new TokenSignatureException(sprintf(
118
                'Token failed signature verification.'
119
            ));
120
        }
121
    }
122
123
    /**
124
     * @throw TokenExpiredException
125
     */
126
    public function ensureNotExpired(Token $token): void
127
    {
128
        if ($this->isExpired($token)) {
129
            throw new TokenExpiredException(sprintf(
130
                'Token validity has expired.'
131
            ));
132
        }
133
    }
134
135
    /**
136
     * @throws TokenValidationExceptionInterface the main one
137
     * @throws TokenParseException
138
     * @throws TokenExpiredException
139
     * @throws TokenSignatureException
140
     * @throws TokenIssuerException
141
     * @throws TokenAudienceException
142
     */
143
    public function getValidatedToken(string $tokenString): Token
144
    {
145
        $token = $this->parseToken($tokenString);
146
147
        $this->ensureValidSignature($token);
148
        $this->ensureNotExpired($token);
149
150
        $data = new ValidationData(); // It will use the current time to validate (iat, nbf and exp)
151
        $data->setIssuer($this->issuer);
152
        $data->setAudience($this->audience);
153
154
        if ($token->hasClaim('iss')) {
155
            $issuer = $token->getClaim('iss', false);
156
            if ($issuer !== $this->issuer) {
157
                throw new TokenIssuerException(sprintf(
158
                    'Token issuer does not match'
159
                ));
160
            }
161
        }
162
163
        if ($token->hasClaim('aud')) {
164
            $issuer = $token->getClaim('aud', false);
165
            if ($issuer !== $this->issuer) {
166
                throw new TokenAudienceException(sprintf(
167
                    'Token audience does not match'
168
                ));
169
            }
170
        }
171
172
        return $token;
173
    }
174
175 1
    public function getSigner(): BaseSigner
176
    {
177 1
        return new Sha256();
178
    }
179
180
    public function verifySignature(Token $token): bool
181
    {
182
        return $token->verify($this->signer, $this->privateKey);
183
    }
184
185
    public function isExpired(Token $token): bool
186
    {
187
        return $token->isExpired();
188
    }
189
}
190