Completed
Push — develop ( 7bb86a...82de8f )
by Wisoot
02:16
created

src/JwtGuard.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace WWON\JwtGuard;
4
5
use Illuminate\Auth\Events\Attempting;
6
use Illuminate\Auth\Events\Login;
7
use Illuminate\Auth\Events\Logout;
8
use Illuminate\Auth\GuardHelpers;
9
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
10
use Illuminate\Contracts\Auth\Guard;
11
use Illuminate\Contracts\Auth\UserProvider;
12
use Illuminate\Http\Request;
13
use Illuminate\Session\TokenMismatchException;
14
use Tymon\JWTAuth\Exceptions\JWTException;
15
use Tymon\JWTAuth\Payload;
16
17
class JwtGuard implements Guard
18
{
19
20
    use GuardHelpers;
21
22
    /**
23
     * @var string
24
     */
25
    protected $token;
26
27
    /**
28
     * @var TokenManager
29
     */
30
    protected $tokenManager;
31
32
    /**
33
     * @var Request
34
     */
35
    protected $request;
36
37
    /**
38
     * Indicates if the logout method has been called.
39
     *
40
     * @var bool
41
     */
42
    protected $loggedOut = false;
43
44
    /**
45
     * JwtGuard constructor
46
     *
47
     * @param UserProvider $provider
48
     * @param TokenManager $tokenManager
49
     * @param Request|null $request
50
     */
51
    public function __construct(
52
        UserProvider $provider,
53
        TokenManager $tokenManager,
54
        Request $request = null
55
    ) {
56
        $this->provider = $provider;
57
        $this->tokenManager = $tokenManager;
58
        $this->request = $request;
59
    }
60
61
    /**
62
     * Get the currently authenticated user.
63
     *
64
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
65
     */
66
    public function user()
67
    {
68
        // If we've already retrieved the user for the current request we can just
69
        // return it back immediately. We do not want to fetch the user data on
70
        // every call to this method because that would be tremendously slow.
71
        if ($this->user) {
72
            return $this->user;
73
        }
74
75
        if (!$token = $this->getBearerToken()) {
76
            return $this->user = null;
77
        }
78
79
        try {
80
            $payload = $this->getPayloadOfToken($token);
81
            $user = $this->getUserByPayload($payload);
82
83
            $this->user = $this->userHasToken($user, $payload) ? $user : null;
84
85
        } catch (JWTException $e) {
86
            $this->user = null;
87
        }
88
89
        return $this->user;
90
    }
91
92
    /**
93
     * Retrieve the payload of the given token.
94
     *
95
     * @param string $token
96
     * @return array
97
     */
98
    protected function getPayloadOfToken($token)
99
    {
100
        return app('tymon.jwt.auth')->setToken($token)->getPayload();
101
    }
102
103
    /**
104
     * Retrieve the user by the given payload.
105
     *
106
     * @param Payload $payload
107
     * @return AuthenticatableContract|null
108
     */
109
    protected function getUserByPayload(Payload $payload)
110
    {
111
        return $this->provider->retrieveById($payload['sub']);
112
    }
113
114
    /**
115
     * Determine whether the user has a token attached.
116
     *
117
     * @param \Illuminate\Contracts\Auth\Authenticatable $user
118
     * @param Payload $payload
119
     * @return bool
120
     */
121
    protected function userHasToken($user, Payload $payload)
122
    {
123
        return $this->tokenManager->check($user->getAuthIdentifier(), $payload['jti']);
124
    }
125
126
    /**
127
     * Remove given token from the given user
128
     * 
129
     * @param $user
130
     * @param Payload $payload
131
     */
132
    protected function removeUserToken($user, Payload $payload)
133
    {
134
        $this->tokenManager->remove($user->getAuthIdentifier(), $payload['jti']);
135
    }
136
137
    /**
138
     * Validate a user's credentials.
139
     *
140
     * @param array $credentials
141
     * @return bool
142
     */
143
    public function validate(array $credentials = [])
144
    {
145
        return $this->attempt($credentials, false);
146
    }
147
148
    /**
149
     * Attempt to authenticate a user using the given credentials.
150
     *
151
     * @param array $credentials
152
     * @param bool $login
153
     * @return bool
154
     */
155
    public function attempt(array $credentials = [], $login = true)
156
    {
157
        $this->fireAttemptEvent($credentials, $login);
158
159
        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
160
161
        // If an implementation of UserInterface was returned, we'll ask the provider
162
        // to validate the user against the given credentials, and if they are in
163
        // fact valid we'll log the users into the application and return true.
164
        if ($this->hasValidCredentials($user, $credentials)) {
165
            if ($login) {
166
                $this->login($user);
167
            }
168
169
            return true;
170
        }
171
172
        return false;
173
    }
174
175
    /**
176
     * Determine if the user matches the credentials.
177
     *
178
     * @param mixed $user
179
     * @param array $credentials
180
     * @return bool
181
     */
182
    protected function hasValidCredentials($user, $credentials)
183
    {
184
        return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
185
    }
186
187
    /**
188
     * Fire the attempt event with the arguments.
189
     *
190
     * @param array $credentials
191
     * @param bool $login
192
     * @return void
193
     */
194
    protected function fireAttemptEvent(array $credentials, $login)
195
    {
196
        if (isset($this->events)) {
197
            $this->events->fire(new Attempting(
198
                $credentials, false, $login
199
            ));
200
        }
201
    }
202
203
    /**
204
     * Register an authentication attempt event listener.
205
     *
206
     * @param mixed $callback
207
     * @return void
208
     */
209
    public function attempting($callback)
210
    {
211
        if (isset($this->events)) {
212
            $this->events->listen(Attempting::class, $callback);
213
        }
214
    }
215
216
    /**
217
     * Log a user into the application.
218
     *
219
     * @param \Illuminate\Contracts\Auth\Authenticatable $user
220
     * @return void
221
     */
222
    public function login(AuthenticatableContract $user)
223
    {
224
        $userId = $user->getAuthIdentifier();
225
        $token = $this->generateTokenForUserId($userId);
226
227
        // If we have an event dispatcher instance set we will fire an event so that
228
        // any listeners will hook into the authentication events and run actions
229
        // based on the login and logout events fired from the guard instances.
230
        $this->fireLoginEvent($user);
231
232
        $this->setToken($token);
233
        $this->setUser($user);
234
    }
235
236
    /**
237
     * generateTokenForUser method
238
     *
239
     * @param int $userId
240
     * @return string
241
     */
242
    protected function generateTokenForUserId($userId)
243
    {
244
        $payload = app('tymon.jwt.payload.factory')->make(['sub' => $userId]);
245
        $token = app('tymon.jwt.auth')->encode($payload);
246
        $this->tokenManager->add($userId, $payload['jti']);
0 ignored issues
show
$userId is of type integer, but the function expects a object<WWON\JwtGuard\Claim>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
247
248
        return $token->get();
249
    }
250
251
    /**
252
     * generateTokenForUser method
253
     *
254
     * @param string $token
255
     * @return string
256
     */
257
    protected function refreshTokenForUser($token)
258
    {
259
        $newToken = app('tymon.jwt.auth')->refresh($token);
260
        $payload = $this->getPayloadOfToken($newToken);
261
        $user = $this->getUserByPayload($payload);
262
263
        $this->tokenManager->add($user->getAuthIdentifier(), $payload['jti']);
264
265
        return $newToken;
266
    }
267
268
    /**
269
     * Fire the login event if the dispatcher is set.
270
     *
271
     * @param \Illuminate\Contracts\Auth\Authenticatable $user
272
     * @param bool  $remember
273
     * @return void
274
     */
275
    protected function fireLoginEvent($user, $remember = false)
276
    {
277
        if (isset($this->events)) {
278
            $this->events->fire(new Login($user, $remember));
279
        }
280
    }
281
282
    /**
283
     * Log the given user ID into the application.
284
     *
285
     * @param mixed $id
286
     * @return \Illuminate\Contracts\Auth\Authenticatable
287
     */
288
    public function loginUsingId($id)
289
    {
290
        $this->login($user = $this->provider->retrieveById($id));
291
292
        return $user;
293
    }
294
295
    /**
296
     * Log the user out of the application.
297
     *
298
     * @return void
299
     */
300 View Code Duplication
    public function logout()
301
    {
302
        if (!$token = $this->getBearerToken()) {
303
            return;
304
        }
305
306
        try {
307
            $payload = $this->getPayloadOfToken($token);
308
309
            if ($user = $this->getUserByPayload($payload)) {
310
                $this->tokenManager->remove($user->getAuthIdentifier(), $payload['jti']);
311
            }
312
313
        } catch (TokenMismatchException $e) { }
314
315
        if (isset($this->events)) {
316
            $this->events->fire(new Logout($this->user));
317
        }
318
319
        // Once we have fired the logout event we will clear the users out of memory
320
        // so they are no longer available as the user is no longer considered as
321
        // being signed into this application and should not be available here.
322
        $this->user = null;
323
        $this->token = null;
324
        $this->loggedOut = true;
325
    }
326
327
    /**
328
     * log this user out from every token
329
     *
330
     * @return void
331
     */
332 View Code Duplication
    public function logoutAll()
333
    {
334
        if (!$token = $this->getBearerToken()) {
335
            return;
336
        }
337
338
        try {
339
            $payload = $this->getPayloadOfToken($token);
340
341
            if ($user = $this->getUserByPayload($payload)) {
342
                $this->tokenManager->removeAll($user->getAuthIdentifier());
343
            }
344
345
        } catch (TokenMismatchException $e) { }
346
347
        if (isset($this->events)) {
348
            $this->events->fire(new Logout($this->user));
349
        }
350
351
        // Once we have fired the logout event we will clear the users out of memory
352
        // so they are no longer available as the user is no longer considered as
353
        // being signed into this application and should not be available here.
354
        $this->user = null;
355
        $this->token = null;
356
        $this->loggedOut = true;
357
    }
358
359
    /**
360
     * Refresh user token
361
     *
362
     * @return string|null
363
     */
364
    public function refreshToken()
365
    {
366
        if (!$token = $this->getBearerToken()) {
367
            return null;
368
        }
369
370
        try {
371
            $payload = $this->getPayloadOfToken($token);
372
            $user = $this->getUserByPayload($payload);
373
374
            $user = $this->userHasToken($user, $payload) ? $user : null;
375
            $this->removeUserToken($user, $payload);
376
377
            $this->token = $this->refreshTokenForUser($token);
378
379
        } catch (JWTException $e) {
380
            return null;
381
        }
382
383
        return $this->token;
384
    }
385
386
    /**
387
     * setToken method
388
     *
389
     * @param string $token
390
     */
391
    public function setToken($token)
392
    {
393
        $this->token = $token;
394
    }
395
396
    /**
397
     * getToken method
398
     *
399
     * @return null|string
400
     */
401
    public function getToken()
402
    {
403
        return $this->token;
404
    }
405
406
    /**
407
     * getBearerToken method
408
     *
409
     * @return string|null
410
     */
411
    protected function getBearerToken()
412
    {
413
        $header = $this->request->header('Authorization', '');
414
415
        if (starts_with(strtolower($header), 'bearer ')) {
416
            return mb_substr($header, 7, null, 'UTF-8');
417
        }
418
    }
419
420
}