1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Tymon\JWTAuth; |
4
|
|
|
|
5
|
|
|
use Illuminate\Http\Request; |
6
|
|
|
use Tymon\JWTAuth\Exceptions\InvalidClaimException; |
7
|
|
|
use Tymon\JWTAuth\Exceptions\JWTException; |
8
|
|
|
use Tymon\JWTAuth\Providers\Auth\AuthInterface; |
9
|
|
|
use Tymon\JWTAuth\Providers\User\UserInterface; |
10
|
|
|
use \Illuminate\Contracts\Auth\Authenticatable; |
11
|
|
|
|
12
|
|
|
class JWTAuth |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* @var \Tymon\JWTAuth\JWTManager |
16
|
|
|
*/ |
17
|
|
|
protected $manager; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var \Tymon\JWTAuth\Providers\User\UserInterface |
21
|
|
|
*/ |
22
|
|
|
protected $user; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var \Tymon\JWTAuth\Providers\Auth\AuthInterface |
26
|
|
|
*/ |
27
|
|
|
protected $auth; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var \Illuminate\Http\Request |
31
|
|
|
*/ |
32
|
|
|
protected $request; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var string |
36
|
|
|
*/ |
37
|
|
|
protected $identifier = 'id'; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var \Tymon\JWTAuth\Token |
41
|
|
|
*/ |
42
|
|
|
protected $token; |
43
|
|
|
|
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var \Illuminate\Contracts\Auth\Authenticatable |
47
|
|
|
*/ |
48
|
|
|
protected $userModel = null; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param \Tymon\JWTAuth\JWTManager $manager |
52
|
|
|
* @param \Tymon\JWTAuth\Providers\User\UserInterface $user |
53
|
|
|
* @param \Tymon\JWTAuth\Providers\Auth\AuthInterface $auth |
54
|
|
|
* @param \Illuminate\Http\Request $request |
55
|
|
|
*/ |
56
|
54 |
|
public function __construct(JWTManager $manager, UserInterface $user, AuthInterface $auth, Request $request) |
|
|
|
|
57
|
|
|
{ |
58
|
54 |
|
$this->manager = $manager; |
59
|
54 |
|
$this->user = $user; |
60
|
54 |
|
$this->auth = $auth; |
61
|
54 |
|
$this->request = $request; |
62
|
54 |
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @return \Illuminate\Contracts\Auth\Authenticatable |
66
|
|
|
*/ |
67
|
|
|
public function getUserModel() |
68
|
|
|
{ |
69
|
|
|
return $this->userModel; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @param \Illuminate\Contracts\Auth\Authenticatable $userModel |
74
|
|
|
*/ |
75
|
|
|
public function setUserModel(Authenticatable $userModel) |
76
|
|
|
{ |
77
|
|
|
$this->userModel = $userModel; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* @param $userModel |
82
|
|
|
*/ |
83
|
12 |
|
private function setUserModelAsObject ($userModel) |
84
|
|
|
{ |
85
|
12 |
|
$this->userModel = $userModel; |
86
|
12 |
|
} |
87
|
|
|
|
88
|
|
|
|
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Find a user using the user identifier in the subject claim. |
92
|
|
|
* |
93
|
|
|
* @param bool|string $token |
94
|
|
|
* |
95
|
|
|
* @return mixed |
96
|
|
|
*/ |
97
|
9 |
|
public function toUser($token = false) |
98
|
|
|
{ |
99
|
9 |
|
$payload = $this->getPayload($token); |
100
|
|
|
|
101
|
6 |
|
if (! $user = $this->user->getBy($this->identifier, $payload['sub'])) { |
102
|
3 |
|
return false; |
103
|
|
|
} |
104
|
|
|
|
105
|
3 |
|
$this->setUserModelAsObject($user); |
106
|
3 |
|
return $user; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Generate a token using the user identifier as the subject claim. |
111
|
|
|
* |
112
|
|
|
* @param mixed $user |
113
|
|
|
* @param array $customClaims |
114
|
|
|
* |
115
|
|
|
* @return string |
116
|
|
|
*/ |
117
|
6 |
|
public function fromUser($user, array $customClaims = []) |
118
|
|
|
{ |
119
|
6 |
|
$payload = $this->makePayload($user->{$this->identifier}, $customClaims); |
120
|
|
|
|
121
|
6 |
|
$ret = $this->manager->encode($payload)->get(); |
122
|
6 |
|
$this->setUserModelAsObject($user); |
123
|
6 |
|
return $ret; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Attempt to authenticate the user and return the token. |
128
|
|
|
* |
129
|
|
|
* @param array $credentials |
130
|
|
|
* @param array $customClaims |
131
|
|
|
* |
132
|
|
|
* @return false|string |
133
|
|
|
* @throws JWTException |
134
|
|
|
*/ |
135
|
6 |
|
public function attempt(array $credentials = [], array $customClaims = []) |
136
|
|
|
{ |
137
|
6 |
|
if (! $this->auth->byCredentials($credentials)) { |
138
|
3 |
|
return false; |
139
|
|
|
} |
140
|
|
|
|
141
|
3 |
|
return $this->fromUser($this->auth->user(), $customClaims); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Authenticate a user via a token. |
146
|
|
|
* |
147
|
|
|
* @param mixed $token |
148
|
|
|
* @param Array $custom custom claims that must be equals (all custom fields indicated must be equals in token, this doesn't entail that the token must have only these claims) |
149
|
|
|
* @return mixed |
150
|
|
|
*/ |
151
|
6 |
|
public function authenticate($token = false, $custom = []) |
152
|
|
|
{ |
153
|
6 |
|
$payload = $this->getPayload($token); |
154
|
6 |
|
$id = $payload->get('sub'); |
155
|
|
|
|
156
|
6 |
|
foreach($custom as $customK => $customV) |
157
|
|
|
if(!isset($payload[$customK]) || $customV != $payload[$customK]) |
158
|
|
|
return new InvalidClaimException('custom fields are wrong'); |
159
|
|
|
|
160
|
|
|
|
161
|
6 |
|
if (! $this->auth->byId($id)) { |
162
|
3 |
|
return false; |
163
|
|
|
} |
164
|
|
|
|
165
|
3 |
|
$user = $this->auth->user(); |
166
|
3 |
|
$this->setUserModelAsObject($user); |
167
|
3 |
|
return $user; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Refresh an expired token. |
172
|
|
|
* |
173
|
|
|
* @param mixed $token |
174
|
|
|
* @param Array $custom |
175
|
|
|
* |
176
|
|
|
* @return string |
177
|
|
|
*/ |
178
|
3 |
|
public function refresh($token = false, $custom = []) |
179
|
|
|
{ |
180
|
3 |
|
$this->requireToken($token); |
181
|
|
|
|
182
|
3 |
|
return $this->manager->refresh($this->token, $custom)->get(); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Invalidate a token (add it to the blacklist). |
187
|
|
|
* |
188
|
|
|
* @param mixed $token |
189
|
|
|
* |
190
|
|
|
* @return boolean |
191
|
|
|
*/ |
192
|
3 |
|
public function invalidate($token = false) |
193
|
|
|
{ |
194
|
3 |
|
$this->requireToken($token); |
195
|
|
|
|
196
|
3 |
|
return $this->manager->invalidate($this->token); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Get the token. |
201
|
|
|
* |
202
|
|
|
* @return boolean|string |
203
|
|
|
*/ |
204
|
12 |
|
public function getToken() |
205
|
|
|
{ |
206
|
12 |
|
if (! $this->token) { |
207
|
|
|
try { |
208
|
6 |
|
$this->parseToken(); |
209
|
5 |
|
} catch (JWTException $e) { |
210
|
3 |
|
return false; |
211
|
|
|
} |
212
|
2 |
|
} |
213
|
|
|
|
214
|
9 |
|
return $this->token; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Get the raw Payload instance. |
219
|
|
|
* |
220
|
|
|
* @param mixed $token |
221
|
|
|
* |
222
|
|
|
* @return \Tymon\JWTAuth\Payload |
223
|
|
|
*/ |
224
|
15 |
|
public function getPayload($token = false) |
225
|
|
|
{ |
226
|
15 |
|
$this->requireToken($token); |
227
|
|
|
|
228
|
12 |
|
return $this->manager->decode($this->token); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Parse the token from the request. |
233
|
|
|
* @param string $method |
234
|
|
|
* @param string $header |
235
|
|
|
* @param string $query |
236
|
|
|
* @return JWTAuth |
237
|
|
|
* @throws JWTException |
238
|
|
|
*/ |
239
|
15 |
|
public function parseToken($method = 'bearer', $header = 'authorization', $query = 'token') |
240
|
|
|
{ |
241
|
15 |
|
if (! $token = $this->parseAuthHeader($header, $method)) { |
242
|
12 |
|
if (! $token = $this->request->query($query, false)) { |
|
|
|
|
243
|
6 |
|
throw new JWTException('The token could not be parsed from the request', 400); |
244
|
|
|
} |
245
|
4 |
|
} |
246
|
|
|
|
247
|
9 |
|
return $this->setToken($token); |
|
|
|
|
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Parse token from the authorization header. |
252
|
|
|
* |
253
|
|
|
* @param string $header |
254
|
|
|
* @param string $method |
255
|
|
|
* |
256
|
|
|
* @return false|string |
257
|
|
|
*/ |
258
|
15 |
|
protected function parseAuthHeader($header = 'authorization', $method = 'bearer') |
259
|
|
|
{ |
260
|
15 |
|
$header = $this->request->headers->get($header); |
261
|
|
|
|
262
|
15 |
|
if (! starts_with(strtolower($header), $method)) { |
263
|
12 |
|
return false; |
264
|
|
|
} |
265
|
|
|
|
266
|
3 |
|
return trim(str_ireplace($method, '', $header)); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Create a Payload instance. |
271
|
|
|
* |
272
|
|
|
* @param mixed $subject |
273
|
|
|
* @param array $customClaims |
274
|
|
|
* |
275
|
|
|
* @return \Tymon\JWTAuth\Payload |
276
|
|
|
*/ |
277
|
6 |
|
protected function makePayload($subject, array $customClaims = []) |
278
|
|
|
{ |
279
|
6 |
|
return $this->manager->getPayloadFactory()->make( |
280
|
6 |
|
array_merge($customClaims, ['sub' => $subject]) |
281
|
4 |
|
); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Set the identifier. |
286
|
|
|
* |
287
|
|
|
* @param string $identifier |
288
|
|
|
* |
289
|
|
|
* @return $this |
290
|
|
|
*/ |
291
|
3 |
|
public function setIdentifier($identifier) |
292
|
|
|
{ |
293
|
3 |
|
$this->identifier = $identifier; |
294
|
|
|
|
295
|
3 |
|
return $this; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* Get the identifier. |
300
|
|
|
* |
301
|
|
|
* @return string |
302
|
|
|
*/ |
303
|
3 |
|
public function getIdentifier() |
304
|
|
|
{ |
305
|
3 |
|
return $this->identifier; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Set the token. |
310
|
|
|
* |
311
|
|
|
* @param string $token |
312
|
|
|
* |
313
|
|
|
* @return $this |
314
|
|
|
*/ |
315
|
27 |
|
public function setToken($token) |
316
|
|
|
{ |
317
|
27 |
|
$this->token = new Token($token); |
318
|
|
|
|
319
|
27 |
|
return $this; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Ensure that a token is available. |
324
|
|
|
* |
325
|
|
|
* @param mixed $token |
326
|
|
|
* |
327
|
|
|
* @return JWTAuth |
328
|
|
|
* |
329
|
|
|
* @throws \Tymon\JWTAuth\Exceptions\JWTException |
330
|
|
|
*/ |
331
|
21 |
|
protected function requireToken($token) |
332
|
|
|
{ |
333
|
21 |
|
if (! $token = $token ?: $this->token) { |
334
|
3 |
|
throw new JWTException('A token is required', 400); |
335
|
|
|
} |
336
|
|
|
|
337
|
18 |
|
return $this->setToken($token); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Set the request instance. |
342
|
|
|
* |
343
|
|
|
* @param Request $request |
344
|
|
|
* @return $this |
345
|
|
|
*/ |
346
|
3 |
|
public function setRequest(Request $request) |
|
|
|
|
347
|
|
|
{ |
348
|
3 |
|
$this->request = $request; |
349
|
|
|
|
350
|
3 |
|
return $this; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* Get the JWTManager instance. |
355
|
|
|
* |
356
|
|
|
* @return \Tymon\JWTAuth\JWTManager |
357
|
|
|
*/ |
358
|
3 |
|
public function manager() |
359
|
|
|
{ |
360
|
3 |
|
return $this->manager; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Magically call the JWT Manager. |
365
|
|
|
* |
366
|
|
|
* @param string $method |
367
|
|
|
* @param array $parameters |
368
|
|
|
* |
369
|
|
|
* @return mixed |
370
|
|
|
* |
371
|
|
|
* @throws \BadMethodCallException |
372
|
|
|
*/ |
373
|
3 |
|
public function __call($method, $parameters) |
374
|
|
|
{ |
375
|
3 |
|
if (method_exists($this->manager, $method)) { |
376
|
3 |
|
return call_user_func_array([$this->manager, $method], $parameters); |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
throw new \BadMethodCallException("Method [$method] does not exist."); |
380
|
|
|
} |
381
|
|
|
} |
382
|
|
|
|