Failed Conditions
Push — master ( 617482...b9a7c3 )
by Sébastien
07:59 queued 12s
created

TokenManager::getDefaultExpiry()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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