Completed
Push — master ( 4dd423...8b4a55 )
by Mathieu
15:57
created

JWTHandler::createBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Charcoal\User\Service;
4
5
use Exception;
6
7
// From psr/http-message (PSR-7)
8
use Psr\Http\Message\ServerRequestInterface as Request;
9
10
// From lcobucci/jwt
11
use Lcobucci\JWT\Builder;
12
use Lcobucci\JWT\Parser;
13
use Lcobucci\JWT\Signer\Rsa\Sha256;
14
use Lcobucci\JWT\Token;
15
use Lcobucci\JWT\ValidationData;
16
17
use Charcoal\User\Config\JWTConfig;
18
19
/**
20
 * Helper class to generate tokens for user and retrieve tokens from request.
21
 */
22
class JWTHandler
23
{
24
    /**
25
     * @var Builder
26
     */
27
    private $builder;
28
29
    /**
30
     * @var \Lcobucci\JWT\Signer
31
     */
32
    private $signer;
33
34
    /**
35
     * @var Parser
36
     */
37
    private $parser;
38
39
    /**
40
     * @var ValidationData
41
     */
42
    private $validationData;
43
44
    /**
45
     * @var string
46
     */
47
    private $privateKey;
48
49
    /**
50
     * @var string
51
     */
52
    private $publicKey;
53
54
    /**
55
     * @param JWTConfig $config The auth / JWT configuration.
56
     */
57
    public function __construct(JWTConfig $config)
58
    {
59
        $this->builder = $this->createBuilder($config);
60
        $this->signer = $this->createSigner();
61
        $this->parser = $this->createParser();
62
        $this->validationData = $this->createValidationData($config);
63
        $this->privateKey = $this->loadPrivateKey($config);
64
        $this->publicKey = $this->loadPublicKey($config);
65
    }
66
67
    /**
68
     * Builds and signs a token with a "uid" claim.
69
     *
70
     * @param string $userId The user to generate the token for.
71
     * @return Token
72
     */
73
    public function generateTokenForUserId($userId)
74
    {
75
        return $this->builder
0 ignored issues
show
Deprecated Code introduced by
The method Lcobucci\JWT\Builder::set() has been deprecated with message: This method will be removed on v4, use with() instead

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

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

Loading history...
Deprecated Code introduced by
The method Lcobucci\JWT\Builder::sign() has been deprecated with message: This method will be removed on v4, signature will be created on the getToken() method

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

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

Loading history...
76
            ->set('uid', $userId)
77
            ->sign($this->signer, $this->privateKey)
78
            ->getToken();
79
    }
80
81
    /**
82
     * Retrieves, parses and validates the token from request's HTTP_AUTHORIZATION header.
83
     *
84
     * @param Request $request A PSR-7 Request.
85
     * @throws Exception If there is no authorization headers in request or the token is invalid.
86
     * @return Token
87
     */
88
    public function getTokenFromRequest(Request $request)
89
    {
90
        $headers = $request->getHeaders();
91
        if (!isset($headers['HTTP_AUTHORIZATION'])) {
92
            throw new Exception(
93
                'No authorization (HTTP_AUTHORIZATION) in request headers.'
94
            );
95
        }
96
        $bearer = str_replace('Bearer ', '', $headers['HTTP_AUTHORIZATION'][0]);
97
        $token = $this->parser->parse($bearer);
98
        if ($this->isTokenValid($token) === false) {
99
            throw new Exception(
100
                'Invalid JWT token.'
101
            );
102
        }
103
        return $token;
104
    }
105
106
    /**
107
     * Validates and verifies a token.
108
     *
109
     * @param Token $token The token to validate and verify.
110
     * @return boolean
111
     */
112
    public function isTokenValid(Token $token)
113
    {
114
        if ($token->validate($this->validationData) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method Lcobucci\JWT\Token::validate() has been deprecated with message: This method will be removed on v4, new validation API should be used

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

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

Loading history...
115
            return false;
116
        }
117
118
        if ($token->verify($this->signer, $this->publicKey) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method Lcobucci\JWT\Token::verify() has been deprecated with message: This method will be removed on v4, new validation API should be used

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

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

Loading history...
119
            return false;
120
        }
121
122
        return true;
123
    }
124
125
    /**
126
     * Retrieves the uid claim (user id) from a token.
127
     *
128
     * @param Token $token The Token to load the user from.
129
     * @throws Exception If the token does not have a user (uid claim) or the user can not be loaded.
130
     * @return string
131
     */
132
    public function getUserIdFromToken(Token $token)
133
    {
134
        if ($token->getClaim('uid') === null) {
0 ignored issues
show
Deprecated Code introduced by
The method Lcobucci\JWT\Token::getClaim() has been deprecated with message: This method will be removed on v4

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

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

Loading history...
135
            throw new Exception(
136
                'Invalid Token. No user (uid claim).'
137
            );
138
        }
139
140
        return $token->getClaim('uid');
0 ignored issues
show
Deprecated Code introduced by
The method Lcobucci\JWT\Token::getClaim() has been deprecated with message: This method will be removed on v4

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

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

Loading history...
141
    }
142
143
    /**
144
     * @param JWTConfig $config The JWT / auth configuration.
145
     * @return Builder
146
     */
147
    private function createBuilder(JWTConfig $config)
148
    {
149
        $builder = new Builder();
150
        $builder
0 ignored issues
show
Deprecated Code introduced by
The method Lcobucci\JWT\Builder::setIssuer() has been deprecated with message: This method will be removed on v4, use issuedBy() instead

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

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

Loading history...
Deprecated Code introduced by
The method Lcobucci\JWT\Builder::setAudience() has been deprecated with message: This method will be removed on v4, use canOnlyBeUsedBy() instead

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

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

Loading history...
151
            ->setIssuer($config['issuer'])
152
            ->setAudience($config['audience'])
153
            ->setId($config['id'], true)
154
            ->setIssuedAt(time())
155
            ->setNotBefore(time())
156
            ->setExpiration((time() + $config['expiration']));
157
        return $builder;
158
    }
159
160
    /**
161
     * @return Parser
162
     */
163
    private function createParser()
164
    {
165
        return new Parser();
166
    }
167
168
    /**
169
     * @return \Lcobucci\JWT\Signer
170
     */
171
    private function createSigner()
172
    {
173
        return new Sha256();
174
    }
175
176
    /**
177
     * @param JWTConfig $config The JWT / auth configuration.
178
     * @return ValidationData
179
     */
180
    private function createValidationData(JWTConfig $config)
181
    {
182
        $validationData = new ValidationData();
0 ignored issues
show
Deprecated Code introduced by
The class Lcobucci\JWT\ValidationData has been deprecated with message: This class will be removed on v4, new validation API should be used

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
183
        $validationData->setIssuer($config['issuer']);
184
        $validationData->setAudience($config['audience']);
185
        $validationData->setId($config['id']);
186
        return $validationData;
187
    }
188
189
    /**
190
     * @param JWTConfig $config The JWT / auth configuration.
191
     * @throws Exception If the key is not set in config or not a string.
192
     * @return string
193
     */
194 View Code Duplication
    private function loadPrivateKey(JWTConfig $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195
    {
196
        if (!isset($config['privateKey'])) {
197
            throw new Exception(
198
                'JWT authentication configuration requires a private key.'
199
            );
200
        }
201
        $keyFile = $config['privateKey'];
202
        if (!file_exists($keyFile)) {
203
            throw new Exception(
204
                sprintf('JWT private key file "%s" does not exist.', $keyFile)
205
            );
206
        }
207
        return file_get_contents($keyFile);
208
    }
209
210
    /**
211
     * @param JWTConfig $config The JWT / auth configuration.
212
     * @throws Exception If the key is not set in config or not a string.
213
     * @return string
214
     */
215 View Code Duplication
    private function loadPublicKey(JWTConfig $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
    {
217
        if (!isset($config['publicKey'])) {
218
            throw new Exception(
219
                'JWT authentication configuration requires a public key.'
220
            );
221
        }
222
223
        $keyFile = $config['publicKey'];
224
        if (!file_exists($keyFile)) {
225
            throw new Exception(
226
                sprintf('JWT public key file "%s" does not exist.', $keyFile)
227
            );
228
        }
229
        return file_get_contents($keyFile);
230
    }
231
}
232