Completed
Push — master ( 54cae1...54f834 )
by claudio
02:18
created

JWTAuth::authenticate()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7.6024

Importance

Changes 5
Bugs 1 Features 0
Metric Value
c 5
b 1
f 0
dl 0
loc 21
ccs 10
cts 13
cp 0.7692
rs 7.551
cc 7
eloc 13
nc 6
nop 2
crap 7.6024
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)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
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 3
    public function getUserModel()
68
    {
69 3
        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
82
    /**
83
     * Find a user using the user identifier in the subject claim.
84
     *
85
     * @param bool|string $token
86
     *
87
     * @return mixed
88
     */
89 9
    public function toUser($token = false)
90
    {
91 9
        if(!$token && $this->getUserModel())
92 6
            return $this->getUserModel();
93
94 9
        $payload = $this->getPayload($token);
95
96 6
        if (! $user = $this->user->getBy($this->identifier, $payload['sub'])) {
97 3
            return false;
98
        }
99
100 3
        $this->setUserModel($user);
101
        return $user;
102
    }
103
104
    /**
105
     * Generate a token using the user identifier as the subject claim.
106
     *
107
     * @param mixed $user
108
     * @param array $customClaims
109
     *
110
     * @return string
111
     */
112 6
    public function fromUser($user, array $customClaims = [])
113
    {
114 6
        $payload = $this->makePayload($user->{$this->identifier}, $customClaims);
115
116 6
        return $this->manager->encode($payload)->get();
117
    }
118
119
    /**
120
     * Attempt to authenticate the user and return the token.
121
     *
122
     * @param array $credentials
123
     * @param array $customClaims
124
     *
125
     * @return false|string
126
     * @throws JWTException
127
     */
128 6
    public function attempt(array $credentials = [], array $customClaims = [])
129
    {
130 6
        if (! $this->auth->byCredentials($credentials)) {
131 3
            return false;
132
        }
133
134 3
        return $this->fromUser($this->auth->user(), $customClaims);
135
    }
136
137
    /**
138
     * Authenticate a user via a token.
139
     *
140
     * @param mixed $token
141
     * @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)
142
     * @return mixed
143
     */
144 6
    public function authenticate($token = false, $custom = [])
145
    {
146 6
        if(!$token && $this->getUserModel())
147 4
            return $this->getUserModel();
148
149 6
        $payload = $this->getPayload($token);
150 6
        $id = $payload->get('sub');
151
152 6
        foreach($custom as $customK => $customV)
153
            if(!isset($payload[$customK]) || $customV != $payload[$customK])
154
                return new InvalidClaimException('custom fields are wrong');
155
156
157 6
        if (! $this->auth->byId($id)) {
158 3
            return false;
159
        }
160
161 3
        $user = $this->auth->user();
162 3
        $this->setUserModel($user);
163
        return $user;
164
    }
165
166
    /**
167
     * Refresh an expired token.
168
     *
169
     * @param mixed $token
170
     * @param Array $custom
171
     *
172
     * @return string
173
     */
174 3
    public function refresh($token = false, $custom = [])
175
    {
176 3
        $this->requireToken($token);
177
178 3
        return $this->manager->refresh($this->token, $custom)->get();
179
    }
180
181
    /**
182
     * Invalidate a token (add it to the blacklist).
183
     *
184
     * @param mixed $token
185
     *
186
     * @return boolean
187
     */
188 3
    public function invalidate($token = false)
189
    {
190 3
        $this->requireToken($token);
191
192 3
        return $this->manager->invalidate($this->token);
193
    }
194
195
    /**
196
     * Get the token.
197
     *
198
     * @return boolean|string
199
     */
200 12
    public function getToken()
201
    {
202 12
        if (! $this->token) {
203
            try {
204 6
                $this->parseToken();
205 5
            } catch (JWTException $e) {
206 3
                return false;
207
            }
208 2
        }
209
210 9
        return $this->token;
211
    }
212
213
    /**
214
     * Get the raw Payload instance.
215
     *
216
     * @param mixed $token
217
     *
218
     * @return \Tymon\JWTAuth\Payload
219
     */
220 15
    public function getPayload($token = false)
221
    {
222 15
        $this->requireToken($token);
223
224 12
        return $this->manager->decode($this->token);
225
    }
226
227
    /**
228
     * Parse the token from the request.
229
     * @param string $method
230
     * @param string $header
231
     * @param string $query
232
     * @return JWTAuth
233
     * @throws JWTException
234
     */
235 15
    public function parseToken($method = 'bearer', $header = 'authorization', $query = 'token')
236
    {
237 15
        if (! $token = $this->parseAuthHeader($header, $method)) {
238 12
            if (! $token = $this->request->query($query, false)) {
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|array|null.

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...
239 6
                throw new JWTException('The token could not be parsed from the request', 400);
240
            }
241 4
        }
242
243 9
        return $this->setToken($token);
0 ignored issues
show
Bug introduced by
It seems like $token can also be of type array; however, Tymon\JWTAuth\JWTAuth::setToken() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
244
    }
245
246
    /**
247
     * Parse token from the authorization header.
248
     *
249
     * @param string $header
250
     * @param string $method
251
     *
252
     * @return false|string
253
     */
254 15
    protected function parseAuthHeader($header = 'authorization', $method = 'bearer')
255
    {
256 15
        $header = $this->request->headers->get($header);
257
258 15
        if (! starts_with(strtolower($header), $method)) {
259 12
            return false;
260
        }
261
262 3
        return trim(str_ireplace($method, '', $header));
263
    }
264
265
    /**
266
     * Create a Payload instance.
267
     *
268
     * @param mixed $subject
269
     * @param array $customClaims
270
     *
271
     * @return \Tymon\JWTAuth\Payload
272
     */
273 6
    protected function makePayload($subject, array $customClaims = [])
274
    {
275 6
        return $this->manager->getPayloadFactory()->make(
276 6
            array_merge($customClaims, ['sub' => $subject])
277 4
        );
278
    }
279
280
    /**
281
     * Set the identifier.
282
     *
283
     * @param string $identifier
284
     *
285
     * @return $this
286
     */
287 3
    public function setIdentifier($identifier)
288
    {
289 3
        $this->identifier = $identifier;
290
291 3
        return $this;
292
    }
293
294
    /**
295
     * Get the identifier.
296
     *
297
     * @return string
298
     */
299 3
    public function getIdentifier()
300
    {
301 3
        return $this->identifier;
302
    }
303
304
    /**
305
     * Set the token.
306
     *
307
     * @param string $token
308
     *
309
     * @return $this
310
     */
311 27
    public function setToken($token)
312
    {
313 27
        $this->token = new Token($token);
314
315 27
        return $this;
316
    }
317
318
    /**
319
     * Ensure that a token is available.
320
     *
321
     * @param mixed $token
322
     *
323
     * @return JWTAuth
324
     *
325
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
326
     */
327 21
    protected function requireToken($token)
328
    {
329 21
        if (! $token = $token ?: $this->token) {
330 3
            throw new JWTException('A token is required', 400);
331
        }
332
333 18
        return $this->setToken($token);
334
    }
335
336
    /**
337
     * Set the request instance.
338
     *
339
     * @param Request $request
340
     * @return $this
341
     */
342 3
    public function setRequest(Request $request)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
343
    {
344 3
        $this->request = $request;
345
346 3
        return $this;
347
    }
348
349
    /**
350
     * Get the JWTManager instance.
351
     *
352
     * @return \Tymon\JWTAuth\JWTManager
353
     */
354 3
    public function manager()
355
    {
356 3
        return $this->manager;
357
    }
358
359
    /**
360
     * Magically call the JWT Manager.
361
     *
362
     * @param string $method
363
     * @param array  $parameters
364
     *
365
     * @return mixed
366
     *
367
     * @throws \BadMethodCallException
368
     */
369 3
    public function __call($method, $parameters)
370
    {
371 3
        if (method_exists($this->manager, $method)) {
372 3
            return call_user_func_array([$this->manager, $method], $parameters);
373
        }
374
375
        throw new \BadMethodCallException("Method [$method] does not exist.");
376
    }
377
}
378