Completed
Push — master ( 62cb82...417c42 )
by Roman
02:58
created

Guard::applyClaims()   B

Complexity

Conditions 6
Paths 14

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 8.5125
cc 6
eloc 13
nc 14
nop 3
1
<?php
2
3
namespace Framgia\Jwt;
4
5
use Carbon\Carbon;
6
use Framgia\Jwt\Contracts\ChecksClaims;
7
use Illuminate\Contracts\Auth\Authenticatable;
8
use Illuminate\Support\Str;
9
use Lcobucci\JWT\Claim;
10
use Lcobucci\JWT\Parser;
11
use Lcobucci\JWT\Builder;
12
use Illuminate\Http\Request;
13
use InvalidArgumentException;
14
use Framgia\Jwt\Contracts\Signer;
15
use Illuminate\Auth\GuardHelpers;
16
use Illuminate\Contracts\Auth\UserProvider;
17
use Framgia\Jwt\Contracts\ProvidesCredentials;
18
use Illuminate\Contracts\Auth\Guard as GuardContract;
19
use Lcobucci\JWT\Token;
20
21
class Guard implements GuardContract
22
{
23
    use GuardHelpers;
24
25
    /**
26
     * Default claims.
27
     * 
28
     * @var array
29
     */
30
    protected $claims = [
31
        'aud' => 'Audience',
32
        'exp' => 'Expiration',
33
        'jti' => 'Id',
34
        'iat' => 'IssuedAt',
35
        'iss' => 'Issuer',
36
        'nbf' => 'NotBefore',
37
        'sub' => 'Subject',
38
    ];
39
40
    /**
41
     * The request instance.
42
     *
43
     * @var \Illuminate\Http\Request
44
     */
45
    protected $request;
46
47
    /**
48
     * @var \Framgia\Jwt\Blacklist
49
     */
50
    protected $blacklist;
51
52
    /**
53
     * @var \Framgia\Jwt\Contracts\Signer
54
     */
55
    protected $signer;
56
57
    /**
58
     * @var \Lcobucci\JWT\Token
59
     */
60
    protected $token;
61
62
    /**
63
     * Create a new authentication guard.
64
     *
65
     * @param  \Illuminate\Contracts\Auth\UserProvider  $provider
66
     * @param  \Illuminate\Http\Request  $request
67
     * @param  \Framgia\Jwt\Blacklist  $blacklist
68
     * @param  \Framgia\Jwt\Contracts\Signer  $signer
69
     */
70
    public function __construct(
71
        UserProvider $provider,
72
        Request $request,
73
        Blacklist $blacklist,
74
        Signer $signer
75
    )
76
    {
77
        $this->request = $request;
78
        $this->provider = $provider;
79
        $this->blacklist = $blacklist;
80
        $this->signer = $signer;
81
    }
82
83
    /**
84
     * Get current token.
85
     * 
86
     * @return \Lcobucci\JWT\Token
87
     */
88
    public function token()
89
    {
90
        if (empty($this->token)) {
91
            $this->token = $this->getTokenForRequest();
92
        }
93
94
        return $this->token;
95
    }
96
97
    /**
98
     * Refresh token expiration with same ID.
99
     * 
100
     * @param  \Lcobucci\JWT\Token|null  $token
101
     * @return \Lcobucci\JWT\Token
102
     */
103
    public function refresh(Token $token = null)
104
    {
105
        if (is_null($token)) {
106
            $token = $this->token();
107
        }
108
        
109
        $builder = $this->applyClaims($token->getClaims());
0 ignored issues
show
Bug introduced by
It seems like $token is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
110
        
111
        $builder->setExpiration($this->getExpirationTimestamp());
112
113
        return $this->signer->sign($builder)->getToken();
114
    }
115
116
    public function setToken(Token $token)
117
    {
118
        $this->token = $token;
119
        $this->user = null;
120
121
        return $this;
122
    }
123
124
    /**
125
     * Get the currently authenticated user.
126
     *
127
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
128
     */
129
    public function user()
130
    {
131
        // If we've already retrieved the user for the current request we can just
132
        // return it back immediately. We do not want to fetch the user data on
133
        // every call to this method because that would be tremendously slow.
134
        if (! is_null($this->user)) {
135
            return $this->user;
136
        }
137
138
        $user = null;
139
140
        $token = $this->token();
141
142
        if (! is_null($token)) {
143
            if ($this->provider instanceof ChecksClaims) {
144
                $user = $this->provider->retrieveByClaims($token->getClaims());
145
            } else {
146
                $user = $this->provider->retrieveById($token->getClaim('sub'));
147
            }
148
        }
149
150
        return $this->user = $user;
151
    }
152
153
    public function setUser(Authenticatable $user)
154
    {
155
        $this->user = $user;
156
        $this->token = $this->createTokenForUser($this->user);
157
158
        return $this;
159
    }
160
161
    /**
162
     * Get the token for the current request.
163
     *
164
     * @return \Lcobucci\JWT\Token
165
     */
166
    protected function getTokenForRequest()
167
    {
168
        $token = $this->request->bearerToken();
169
170
        if (empty($token)) {
171
            return null;
172
        }
173
174
        try {
175
            $token = (new Parser())->parse($token);
176
177
            if (!$this->signer->verify($token)) {
178
                return null;
179
            }
180
        } catch (InvalidArgumentException $e) {
181
            return null;
182
        }
183
184
        return $token;
185
    }
186
187
    /**
188
     * Validate a user's credentials.
189
     *
190
     * @param  array  $credentials
191
     * @return bool
192
     */
193
    public function validate(array $credentials = [])
194
    {
195
        $user = $this->provider->retrieveByCredentials($credentials);
196
        if (!is_null($user) && $this->provider->validateCredentials($user, $credentials)) {
197
            $this->user = $user;
198
            return true;
199
        }
200
201
        return false;
202
    }
203
204
    /**
205
     * @param array $credentials
206
     * @return \Lcobucci\JWT\Token|null
207
     */
208
    public function attempt(array $credentials)
209
    {
210
        if (!$this->validate($credentials)) {
211
            return null;
212
        }
213
214
        return $this->token = $this->createTokenForUser($this->user);
215
    }
216
217
    /**
218
     * @param  Authenticatable  $user
219
     * @return Token
220
     */
221
    public function createTokenForUser(Authenticatable $user)
222
    {
223
        $builder = new Builder();
224
225
        $id = $user->getAuthIdentifier();
226
        $builder->setSubject($id);
227
228
        if ($user instanceof ProvidesCredentials) {
229
            $builder = $this->applyClaims($user->getCredentials(), true, $builder);
230
        }
231
232
        $builder->setExpiration($this->getExpirationTimestamp());
233
234
        $builder->setId(Str::random());
235
236
        return $this->signer->sign($builder)->getToken();
237
    }
238
239
    /**
240
     * @return bool
241
     */
242
    public function logout()
243
    {
244
        $token = $this->getTokenForRequest();
245
246
        if (empty($token)) {
247
            $result = true;
248
        } else {
249
            $result = $this->blacklist->add($token);
250
        }
251
252
        if ($result) {
253
            $this->token = null;
254
            $this->user = null;
255
        }
256
257
        return $result;
258
    }
259
260
    /**
261
     * Set the current request instance.
262
     *
263
     * @param  \Illuminate\Http\Request  $request
264
     * @return $this
265
     */
266
    public function setRequest(Request $request)
267
    {
268
        $this->request = $request;
269
270
        return $this;
271
    }
272
273
    /**
274
     * Apply claims to builder.
275
     * 
276
     * @param  array  $claims
277
     * @param  bool  $protect
278
     * @param  \Lcobucci\JWT\Builder|null  $builder
279
     * @return \Lcobucci\JWT\Builder
280
     */
281
    protected function applyClaims(array $claims, $protect = false, Builder $builder = null)
282
    {
283
        if (is_null($builder)) {
284
            $builder = new Builder();
285
        }
286
287
        foreach ($claims as $key => $value) {
288
289
            if ($value instanceof Claim) {
290
                $key = $value->getName();
291
                $value = $value->getValue();
292
            }
293
294
            if (array_key_exists($key, $this->claims)) {
295
                if (!$protect) {
296
                    $builder->{'set' . $this->claims[$key]}($value);
297
                }
298
            } else {
299
                $builder->set($key, $value);
300
            }
301
        }
302
303
        return $builder;
304
    }
305
306
    /**
307
     * Get token expiration timestamp.
308
     * 
309
     * @return int
310
     */
311
    protected function getExpirationTimestamp()
312
    {
313
        return Carbon::now()->addDay()->timestamp;
314
    }
315
}
316