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

src/JwtGuard.php (2 issues)

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
0 ignored issues
show
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: check, guest, id, setUser
Loading history...
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;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->user; of type WWON\JwtGuard\Illuminate...th\Authenticatable|null adds the type WWON\JwtGuard\Illuminate...ts\Auth\Authenticatable to the return on line 89 which is incompatible with the return type declared by the interface Illuminate\Contracts\Auth\Guard::user of type Illuminate\Contracts\Auth\Authenticatable|null.
Loading history...
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    Tymon\JWTAuth\Payload    $payload
107
     * @return    Illuminate\Contracts\Auth\Authenticatable
108
     */
109
    protected function getUserByPayload(Payload $payload)
110
    {
111
        return $this->provider->retrieveById($payload['sub']);
112
    }
113
114
    /**
115
     * Retrieve the user by the given token.
116
     *
117
     * @param    string    $token
118
     * @return    Illuminate\Contracts\Auth\Authenticatable
119
     */
120
    public function getUserByToken($token)
121
    {
122
        $payload = $this->getPayloadOfToken($token);
123
124
        return $this->getUserByPayload($payload);
125
    }
126
127
    /**
128
     * Determine whether the user has a token attached.
129
     *
130
     * @param \Illuminate\Contracts\Auth\Authenticatable   $user
131
     * @param  Tymon\JWTAuth\Payload  $payload
132
     * @return    boolean
133
     */
134
    protected function userHasToken($user, Payload $payload)
135
    {
136
        return $this->tokenManager->check($user->getAuthIdentifier(), $payload['jti']);
137
    }
138
139
    /**
140
     * Validate a user's credentials.
141
     *
142
     * @param  array  $credentials
143
     * @return bool
144
     */
145
    public function validate(array $credentials = [])
146
    {
147
        return $this->attempt($credentials, false);
148
    }
149
150
    /**
151
     * Attempt to authenticate a user using the given credentials.
152
     *
153
     * @param  array  $credentials
154
     * @param  bool   $login
155
     * @return bool
156
     */
157
    public function attempt(array $credentials = [], $login = true)
158
    {
159
        $this->fireAttemptEvent($credentials, $login);
160
161
        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
162
163
        // If an implementation of UserInterface was returned, we'll ask the provider
164
        // to validate the user against the given credentials, and if they are in
165
        // fact valid we'll log the users into the application and return true.
166
        if ($this->hasValidCredentials($user, $credentials)) {
167
            if ($login) {
168
                $this->login($user);
169
            }
170
171
            return true;
172
        }
173
174
        return false;
175
    }
176
177
    /**
178
     * Determine if the user matches the credentials.
179
     *
180
     * @param  mixed  $user
181
     * @param  array  $credentials
182
     * @return bool
183
     */
184
    protected function hasValidCredentials($user, $credentials)
185
    {
186
        return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
187
    }
188
189
    /**
190
     * Fire the attempt event with the arguments.
191
     *
192
     * @param  array  $credentials
193
     * @param  bool  $login
194
     * @return void
195
     */
196
    protected function fireAttemptEvent(array $credentials, $login)
197
    {
198
        if (isset($this->events)) {
199
            $this->events->fire(new Attempting(
200
                $credentials, false, $login
201
            ));
202
        }
203
    }
204
205
    /**
206
     * Register an authentication attempt event listener.
207
     *
208
     * @param  mixed  $callback
209
     * @return void
210
     */
211
    public function attempting($callback)
212
    {
213
        if (isset($this->events)) {
214
            $this->events->listen(Attempting::class, $callback);
215
        }
216
    }
217
218
    /**
219
     * Log a user into the application.
220
     *
221
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
222
     * @return void
223
     */
224
    public function login(AuthenticatableContract $user)
225
    {
226
        $userId = $user->getAuthIdentifier();
227
        $token = $this->generateTokenForUserId($userId);
228
229
        // If we have an event dispatcher instance set we will fire an event so that
230
        // any listeners will hook into the authentication events and run actions
231
        // based on the login and logout events fired from the guard instances.
232
        $this->fireLoginEvent($user);
233
234
        $this->setToken($token);
235
        $this->setUser($user);
236
    }
237
238
    /**
239
     * generateTokenForUser method
240
     *
241
     * @param int $userId
242
     * @return string
243
     */
244
    protected function generateTokenForUserId($userId)
245
    {
246
        $payload = app('tymon.jwt.payload.factory')->make(['sub' => $userId]);
247
        $token = app('tymon.jwt.auth')->encode($payload);
248
        $this->tokenManager->add($userId, $payload['jti']);
249
250
        return $token->get();
251
    }
252
253
    /**
254
     * Fire the login event if the dispatcher is set.
255
     *
256
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
257
     * @param  bool  $remember
258
     * @return void
259
     */
260
    protected function fireLoginEvent($user, $remember = false)
261
    {
262
        if (isset($this->events)) {
263
            $this->events->fire(new Login($user, $remember));
264
        }
265
    }
266
267
    /**
268
     * Log the given user ID into the application.
269
     *
270
     * @param  mixed  $id
271
     * @return \Illuminate\Contracts\Auth\Authenticatable
272
     */
273
    public function loginUsingId($id)
274
    {
275
        $this->login($user = $this->provider->retrieveById($id));
276
277
        return $user;
278
    }
279
280
    /**
281
     * Log the user out of the application.
282
     *
283
     * @return void
284
     */
285 View Code Duplication
    public function logout()
286
    {
287
        if (!$token = $this->getBearerToken()) {
288
            return;
289
        }
290
291
        try {
292
            $payload = $this->getPayloadOfToken($token);
293
294
            if ($user = $this->getUserByPayload($payload)) {
295
                $this->tokenManager->remove($user->getAuthIdentifier(), $payload['jti']);
296
            }
297
298
        } catch (TokenMismatchException $e) { }
299
300
        if (isset($this->events)) {
301
            $this->events->fire(new Logout($this->user));
302
        }
303
304
        // Once we have fired the logout event we will clear the users out of memory
305
        // so they are no longer available as the user is no longer considered as
306
        // being signed into this application and should not be available here.
307
        $this->user = null;
308
        $this->token = null;
309
        $this->loggedOut = true;
310
    }
311
312
    /**
313
     * log this user out from every token
314
     *
315
     * @return void
316
     */
317 View Code Duplication
    public function logoutAll()
318
    {
319
        if (!$token = $this->getBearerToken()) {
320
            return;
321
        }
322
323
        try {
324
            $payload = $this->getPayloadOfToken($token);
325
326
            if ($user = $this->getUserByPayload($payload)) {
327
                $this->tokenManager->removeAll($user->getAuthIdentifier());
328
            }
329
330
        } catch (TokenMismatchException $e) { }
331
332
        if (isset($this->events)) {
333
            $this->events->fire(new Logout($this->user));
334
        }
335
336
        // Once we have fired the logout event we will clear the users out of memory
337
        // so they are no longer available as the user is no longer considered as
338
        // being signed into this application and should not be available here.
339
        $this->user = null;
340
        $this->token = null;
341
        $this->loggedOut = true;
342
    }
343
344
    /**
345
     * setToken method
346
     *
347
     * @param string $token
348
     */
349
    public function setToken($token)
350
    {
351
        $this->token = $token;
352
    }
353
354
    /**
355
     * getToken method
356
     *
357
     * @return null|string
358
     */
359
    public function getToken()
360
    {
361
        return $this->token;
362
    }
363
364
    /**
365
     * getBearerToken method
366
     *
367
     * @return string|null
368
     */
369
    protected function getBearerToken()
370
    {
371
        $header = $this->request->header('Authorization', '');
372
373
        if (starts_with(strtolower($header), 'bearer ')) {
374
            return mb_substr($header, 7, null, 'UTF-8');
375
        }
376
    }
377
378
}