Completed
Push — master ( cd0449...6f7f25 )
by claudio
02:13
created

JWTAuth::setUserModelAsObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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
     * @param \stdClass $userModel
82
     */
83 6
    private function setUserModelAsObject (\stdClass $userModel)
84
    {
85 6
        $this->userModel = $userModel;
0 ignored issues
show
Documentation Bug introduced by
It seems like $userModel of type object<stdClass> is incompatible with the declared type object<Illuminate\Contracts\Auth\Authenticatable> of property $userModel.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
86 6
    }
87
88
89
90
    /**
91
     * Find a user using the user identifier in the subject claim.
92
     *
93
     * @param bool|string $token
94
     *
95
     * @return mixed
96
     */
97 9
    public function toUser($token = false)
98
    {
99 9
        if(!$token && $this->getUserModel())
100 6
            return $this->getUserModel();
101
102 9
        $payload = $this->getPayload($token);
103
104 6
        if (! $user = $this->user->getBy($this->identifier, $payload['sub'])) {
105 3
            return false;
106
        }
107
108 3
        $this->setUserModelAsObject($user);
109 3
        return $user;
110
    }
111
112
    /**
113
     * Generate a token using the user identifier as the subject claim.
114
     *
115
     * @param mixed $user
116
     * @param array $customClaims
117
     *
118
     * @return string
119
     */
120 6
    public function fromUser($user, array $customClaims = [])
121
    {
122 6
        $payload = $this->makePayload($user->{$this->identifier}, $customClaims);
123
124 6
        return $this->manager->encode($payload)->get();
125
    }
126
127
    /**
128
     * Attempt to authenticate the user and return the token.
129
     *
130
     * @param array $credentials
131
     * @param array $customClaims
132
     *
133
     * @return false|string
134
     * @throws JWTException
135
     */
136 6
    public function attempt(array $credentials = [], array $customClaims = [])
137
    {
138 6
        if (! $this->auth->byCredentials($credentials)) {
139 3
            return false;
140
        }
141
142 3
        return $this->fromUser($this->auth->user(), $customClaims);
143
    }
144
145
    /**
146
     * Authenticate a user via a token.
147
     *
148
     * @param mixed $token
149
     * @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)
150
     * @return mixed
151
     */
152 6
    public function authenticate($token = false, $custom = [])
153
    {
154 6
        if(!$token && $this->getUserModel())
155 4
            return $this->getUserModel();
156
157 6
        $payload = $this->getPayload($token);
158 6
        $id = $payload->get('sub');
159
160 6
        foreach($custom as $customK => $customV)
161
            if(!isset($payload[$customK]) || $customV != $payload[$customK])
162
                return new InvalidClaimException('custom fields are wrong');
163
164
165 6
        if (! $this->auth->byId($id)) {
166 3
            return false;
167
        }
168
169 3
        $user = $this->auth->user();
170 3
        $this->setUserModelAsObject($user);
171 3
        return $user;
172
    }
173
174
    /**
175
     * Refresh an expired token.
176
     *
177
     * @param mixed $token
178
     * @param Array $custom
179
     *
180
     * @return string
181
     */
182 3
    public function refresh($token = false, $custom = [])
183
    {
184 3
        $this->requireToken($token);
185
186 3
        return $this->manager->refresh($this->token, $custom)->get();
187
    }
188
189
    /**
190
     * Invalidate a token (add it to the blacklist).
191
     *
192
     * @param mixed $token
193
     *
194
     * @return boolean
195
     */
196 3
    public function invalidate($token = false)
197
    {
198 3
        $this->requireToken($token);
199
200 3
        return $this->manager->invalidate($this->token);
201
    }
202
203
    /**
204
     * Get the token.
205
     *
206
     * @return boolean|string
207
     */
208 12
    public function getToken()
209
    {
210 12
        if (! $this->token) {
211
            try {
212 6
                $this->parseToken();
213 5
            } catch (JWTException $e) {
214 3
                return false;
215
            }
216 2
        }
217
218 9
        return $this->token;
219
    }
220
221
    /**
222
     * Get the raw Payload instance.
223
     *
224
     * @param mixed $token
225
     *
226
     * @return \Tymon\JWTAuth\Payload
227
     */
228 15
    public function getPayload($token = false)
229
    {
230 15
        $this->requireToken($token);
231
232 12
        return $this->manager->decode($this->token);
233
    }
234
235
    /**
236
     * Parse the token from the request.
237
     * @param string $method
238
     * @param string $header
239
     * @param string $query
240
     * @return JWTAuth
241
     * @throws JWTException
242
     */
243 15
    public function parseToken($method = 'bearer', $header = 'authorization', $query = 'token')
244
    {
245 15
        if (! $token = $this->parseAuthHeader($header, $method)) {
246 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...
247 6
                throw new JWTException('The token could not be parsed from the request', 400);
248
            }
249 4
        }
250
251 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...
252
    }
253
254
    /**
255
     * Parse token from the authorization header.
256
     *
257
     * @param string $header
258
     * @param string $method
259
     *
260
     * @return false|string
261
     */
262 15
    protected function parseAuthHeader($header = 'authorization', $method = 'bearer')
263
    {
264 15
        $header = $this->request->headers->get($header);
265
266 15
        if (! starts_with(strtolower($header), $method)) {
267 12
            return false;
268
        }
269
270 3
        return trim(str_ireplace($method, '', $header));
271
    }
272
273
    /**
274
     * Create a Payload instance.
275
     *
276
     * @param mixed $subject
277
     * @param array $customClaims
278
     *
279
     * @return \Tymon\JWTAuth\Payload
280
     */
281 6
    protected function makePayload($subject, array $customClaims = [])
282
    {
283 6
        return $this->manager->getPayloadFactory()->make(
284 6
            array_merge($customClaims, ['sub' => $subject])
285 4
        );
286
    }
287
288
    /**
289
     * Set the identifier.
290
     *
291
     * @param string $identifier
292
     *
293
     * @return $this
294
     */
295 3
    public function setIdentifier($identifier)
296
    {
297 3
        $this->identifier = $identifier;
298
299 3
        return $this;
300
    }
301
302
    /**
303
     * Get the identifier.
304
     *
305
     * @return string
306
     */
307 3
    public function getIdentifier()
308
    {
309 3
        return $this->identifier;
310
    }
311
312
    /**
313
     * Set the token.
314
     *
315
     * @param string $token
316
     *
317
     * @return $this
318
     */
319 27
    public function setToken($token)
320
    {
321 27
        $this->token = new Token($token);
322
323 27
        return $this;
324
    }
325
326
    /**
327
     * Ensure that a token is available.
328
     *
329
     * @param mixed $token
330
     *
331
     * @return JWTAuth
332
     *
333
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
334
     */
335 21
    protected function requireToken($token)
336
    {
337 21
        if (! $token = $token ?: $this->token) {
338 3
            throw new JWTException('A token is required', 400);
339
        }
340
341 18
        return $this->setToken($token);
342
    }
343
344
    /**
345
     * Set the request instance.
346
     *
347
     * @param Request $request
348
     * @return $this
349
     */
350 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...
351
    {
352 3
        $this->request = $request;
353
354 3
        return $this;
355
    }
356
357
    /**
358
     * Get the JWTManager instance.
359
     *
360
     * @return \Tymon\JWTAuth\JWTManager
361
     */
362 3
    public function manager()
363
    {
364 3
        return $this->manager;
365
    }
366
367
    /**
368
     * Magically call the JWT Manager.
369
     *
370
     * @param string $method
371
     * @param array  $parameters
372
     *
373
     * @return mixed
374
     *
375
     * @throws \BadMethodCallException
376
     */
377 3
    public function __call($method, $parameters)
378
    {
379 3
        if (method_exists($this->manager, $method)) {
380 3
            return call_user_func_array([$this->manager, $method], $parameters);
381
        }
382
383
        throw new \BadMethodCallException("Method [$method] does not exist.");
384
    }
385
}
386