TokenManager::ensureNotExpired()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 5
ccs 0
cts 4
cp 0
crap 6
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 1
    public function __construct(
48
        string $privateKey,
49
        int $defaultExpiry = self::DEFAULT_EXPIRY,
50
        string $issuer = null,
51
        string $audience = null
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())
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::setExpiration() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

67
        $builder = /** @scrutinizer ignore-deprecated */ (new Builder())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::setId() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

67
        $builder = /** @scrutinizer ignore-deprecated */ (new Builder())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::setIssuedAt() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

67
        $builder = /** @scrutinizer ignore-deprecated */ (new Builder())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::setNotBefore() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

67
        $builder = /** @scrutinizer ignore-deprecated */ (new Builder())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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);
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::setAudience() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

74
            /** @scrutinizer ignore-deprecated */ $builder->setAudience($this->audience);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
75
        }
76
77
        if ($this->issuer !== null) {
78
            $builder->setIssuer($this->issuer);
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::setIssuer() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

78
            /** @scrutinizer ignore-deprecated */ $builder->setIssuer($this->issuer);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
79
        }
80
81
        foreach ($customClaims as $key => $value) {
82
            $builder->set($key, $value);
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::set() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

82
            /** @scrutinizer ignore-deprecated */ $builder->set($key, $value);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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)
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Builder::sign() has been deprecated: This method will be removed on v4 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

97
        return /** @scrutinizer ignore-deprecated */ $builder->sign($this->signer, $this->privateKey)

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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 1
    public function getSigner(): BaseSigner
189
    {
190 1
        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