Completed
Branch develop (120cb2)
by Wisoot
02:02
created

JwtGuard::removeUserToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
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);
0 ignored issues
show
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
82
83
            $this->user = $this->userHasToken($user, $payload) ? $user : null;
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->getUserByPayload($payload) on line 81 can be null; however, WWON\JwtGuard\JwtGuard::userHasToken() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
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);
0 ignored issues
show
Bug introduced by
The property lastAttempted does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->provider->retriev...edentials($credentials) on line 159 can be null; however, WWON\JwtGuard\JwtGuard::login() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
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(
0 ignored issues
show
Bug introduced by
The property events does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
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']);
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);
0 ignored issues
show
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
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));
0 ignored issues
show
Bug introduced by
It seems like $user = $this->provider->retrieveById($id) can be null; however, login() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
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()
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...
301
    {
302
        if (!$token = $this->getBearerToken()) {
303
            return;
304
        }
305
306
        try {
307
            $payload = $this->getPayloadOfToken($token);
308
309
            if ($user = $this->getUserByPayload($payload)) {
0 ignored issues
show
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
310
                $this->tokenManager->remove($user->getAuthIdentifier(), $payload['jti']);
311
            }
312
313
        } catch (TokenMismatchException $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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()
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...
333
    {
334
        if (!$token = $this->getBearerToken()) {
335
            return;
336
        }
337
338
        try {
339
            $payload = $this->getPayloadOfToken($token);
340
341
            if ($user = $this->getUserByPayload($payload)) {
0 ignored issues
show
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
342
                $this->tokenManager->removeAll($user->getAuthIdentifier());
343
            }
344
345
        } catch (TokenMismatchException $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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);
0 ignored issues
show
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
373
374
            $user = $this->userHasToken($user, $payload) ? $user : null;
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userHasToken($use...payload) ? $user : null on line 374 can be null; however, WWON\JwtGuard\JwtGuard::userHasToken() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
375
            $this->removeUserToken($user, $payload);
0 ignored issues
show
Documentation introduced by
$payload is of type array, but the function expects a object<Tymon\JWTAuth\Payload>.

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...
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
}