Completed
Push — develop ( 8c7de8...b64521 )
by Wisoot
02:54
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\Support\Facades\Config;
14
use WWON\JwtGuard\Exceptions\Exception;
15
use WWON\JwtGuard\Exceptions\InaccessibleException;
16
use WWON\JwtGuard\Exceptions\InvalidTokenException;
17
use WWON\JwtGuard\Exceptions\MalformedException;
18
use WWON\JwtGuard\Exceptions\TokenExpiredException;
19
20
class JwtGuard implements Guard
21
{
22
23
    use GuardHelpers;
24
25
    /**
26
     * @var string
27
     */
28
    protected $token;
29
30
    /**
31
     * @var bool
32
     */
33
    protected $isTokenRefreshable = false;
34
35
    /**
36
     * @var JwtService
37
     */
38
    protected $jwtService;
39
40
    /**
41
     * @var Request
42
     */
43
    protected $request;
44
45
    /**
46
     * Indicates if the logout method has been called.
47
     *
48
     * @var bool
49
     */
50
    protected $loggedOut = false;
51
52
    /**
53
     * JwtGuard constructor
54
     *
55
     * @param UserProvider $provider
56
     * @param JwtService $jwtService
57
     * @param Request|null $request
58
     */
59
    public function __construct(
60
        UserProvider $provider,
61
        JwtService $jwtService,
62
        Request $request = null
63
    ) {
64
        $this->provider = $provider;
65
        $this->jwtService = $jwtService;
66
        $this->request = $request;
67
    }
68
69
    /**
70
     * Get the currently authenticated user.
71
     *
72
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
73
     */
74
    public function user()
75
    {
76
        // If we've already retrieved the user for the current request we can just
77
        // return it back immediately. We do not want to fetch the user data on
78
        // every call to this method because that would be tremendously slow.
79
        if ($this->user) {
80
            return $this->user;
81
        }
82
83
        if (!$token = $this->getBearerToken()) {
84
            return $this->user = null;
85
        }
86
87
        try {
88
            $this->user = $this->getUserByToken($token);
89
        } catch (InaccessibleException $e) {
90
            $this->isTokenRefreshable = true;
91
            $this->user = null;
92
        } catch (Exception $e) {
93
            $this->user = null;
94
        }
95
96
        return $this->user;
97
    }
98
99
    /**
100
     * Retrieve the user by the given payload.
101
     *
102
     * @param string $token
103
     * @return AuthenticatableContract|null
104
     * @throws InaccessibleException
105
     * @throws MalformedException
106
     * @throws TokenExpiredException
107
     * @throws InvalidTokenException
108
     */
109
    protected function getUserByToken($token)
110
    {
111
        $claim = $this->jwtService->getClaimFromToken($token);
112
        $user = $this->provider->retrieveById($claim->sub);
113
114
        if (!empty($user) && get_class($user) !== $claim->aud) {
115
            throw new InvalidTokenException;
116
        }
117
118
        return $user;
119
    }
120
121
    /**
122
     * Validate a user's credentials.
123
     *
124
     * @param array $credentials
125
     * @return bool
126
     */
127
    public function validate(array $credentials = [])
128
    {
129
        return $this->attempt($credentials, false);
130
    }
131
132
    /**
133
     * Attempt to authenticate a user using the given credentials.
134
     *
135
     * @param array $credentials
136
     * @param bool $login
137
     * @return bool
138
     */
139
    public function attempt(array $credentials = [], $login = true)
140
    {
141
        $this->fireAttemptEvent($credentials, $login);
142
143
        $user = $this->provider->retrieveByCredentials($credentials);
144
145
        // If an implementation of UserInterface was returned, we'll ask the provider
146
        // to validate the user against the given credentials, and if they are in
147
        // fact valid we'll log the users into the application and return true.
148
        if ($this->hasValidCredentials($user, $credentials)) {
149
            if ($login) {
150
                $this->login($user);
151
            }
152
153
            return true;
154
        }
155
156
        return false;
157
    }
158
159
    /**
160
     * Determine if the user matches the credentials.
161
     *
162
     * @param mixed $user
163
     * @param array $credentials
164
     * @return bool
165
     */
166
    protected function hasValidCredentials($user, $credentials)
167
    {
168
        return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
169
    }
170
171
    /**
172
     * Fire the attempt event with the arguments.
173
     *
174
     * @param array $credentials
175
     * @param bool $login
176
     * @return void
177
     */
178
    protected function fireAttemptEvent(array $credentials, $login)
179
    {
180
        if (isset($this->events)) {
181
            $this->events->fire(new Attempting(
182
                $credentials, false, $login
183
            ));
184
        }
185
    }
186
187
    /**
188
     * Register an authentication attempt event listener.
189
     *
190
     * @param mixed $callback
191
     * @return void
192
     */
193
    public function attempting($callback)
194
    {
195
        if (isset($this->events)) {
196
            $this->events->listen(Attempting::class, $callback);
197
        }
198
    }
199
200
    /**
201
     * Log a user into the application.
202
     *
203
     * @param \Illuminate\Contracts\Auth\Authenticatable $user
204
     * @return void
205
     */
206
    public function login(AuthenticatableContract $user)
207
    {
208
        $claim = new Claim([
209
            'sub' => $user->getAuthIdentifier(),
210
            'aud' => get_class($user),
211
            'refresh' => Config::get('jwt.refreshable')
212
        ]);
213
214
        $token = $this->jwtService->getTokenForClaim($claim);
215
216
        // If we have an event dispatcher instance set we will fire an event so that
217
        // any listeners will hook into the authentication events and run actions
218
        // based on the login and logout events fired from the guard instances.
219
        $this->fireLoginEvent($user);
220
221
        $this->setToken($token);
222
        $this->setUser($user);
223
    }
224
225
    /**
226
     * generateTokenForUser method
227
     *
228
     * @param string $token
229
     * @return string
230
     */
231
    protected function refreshTokenForUser($token)
232
    {
233
        try {
234
            $newToken = $this->jwtService->refreshToken($token);
235
        } catch (Exception $e) {
236
            $newToken = null;
237
        }
238
239
        return $newToken;
240
    }
241
242
    /**
243
     * Fire the login event if the dispatcher is set.
244
     *
245
     * @param \Illuminate\Contracts\Auth\Authenticatable $user
246
     * @param bool  $remember
247
     * @return void
248
     */
249
    protected function fireLoginEvent($user, $remember = false)
250
    {
251
        if (isset($this->events)) {
252
            $this->events->fire(new Login($user, $remember));
253
        }
254
    }
255
256
    /**
257
     * Log the given user ID into the application.
258
     *
259
     * @param mixed $id
260
     * @return \Illuminate\Contracts\Auth\Authenticatable
261
     */
262
    public function loginUsingId($id)
263
    {
264
        $this->login($user = $this->provider->retrieveById($id));
265
266
        return $user;
267
    }
268
269
    /**
270
     * Log the user out of the application.
271
     *
272
     * @return void
273
     */
274
    public function logout()
275
    {
276
        if (!$token = $this->getBearerToken()) {
277
            return;
278
        }
279
280
        try {
281
            $this->jwtService->invalidateToken($token);
282
        } catch (Exception $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
283
284
        $this->logoutCurrentUser();
285
    }
286
287
    /**
288
     * log this user out from every token
289
     *
290
     * @return void
291
     */
292
    public function logoutAll()
293
    {
294
        if (!$token = $this->getBearerToken()) {
295
            return;
296
        }
297
298
        try {
299
            $user = $this->jwtService->getClaimFromToken($token);
300
301
            $this->jwtService->wipeUserTokens($user);
302
303
        } catch (Exception $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
304
305
        $this->logoutCurrentUser();
306
    }
307
308
    /**
309
     * logoutCurrentUser method
310
     */
311
    protected function logoutCurrentUser()
312
    {
313
        if (isset($this->events)) {
314
            $this->events->fire(new Logout($this->user));
315
        }
316
317
        // Once we have fired the logout event we will clear the users out of memory
318
        // so they are no longer available as the user is no longer considered as
319
        // being signed into this application and should not be available here.
320
        $this->user = null;
321
        $this->token = null;
322
        $this->loggedOut = true;
323
    }
324
325
    /**
326
     * Refresh user token
327
     *
328
     * @return string|null
329
     */
330
    public function refreshToken()
331
    {
332
        if (!$token = $this->getBearerToken()) {
333
            return null;
334
        }
335
336
        $this->token = $this->refreshTokenForUser($token);
337
338
        return $this->token;
339
    }
340
341
    /**
342
     * setToken method
343
     *
344
     * @param string $token
345
     */
346
    public function setToken($token)
347
    {
348
        $this->token = $token;
349
    }
350
351
    /**
352
     * getToken method
353
     *
354
     * @return null|string
355
     */
356
    public function getToken()
357
    {
358
        return $this->token;
359
    }
360
361
    /**
362
     * isTokenRefreshable method
363
     */
364
    public function isTokenRefreshable()
365
    {
366
        return $this->isTokenRefreshable;
367
    }
368
369
    /**
370
     * getBearerToken method
371
     *
372
     * @return string|null
373
     */
374
    protected function getBearerToken()
375
    {
376
        $header = $this->request->header('Authorization', '');
377
378
        if (starts_with(strtolower($header), 'bearer ')) {
379
            return mb_substr($header, 7, null, 'UTF-8');
380
        }
381
382
        return null;
383
    }
384
385
}