Issues (10)

src/JWTGuard.php (1 issue)

1
<?php
2
3
4
namespace JWTAuth;
5
6
use Illuminate\Auth\EloquentUserProvider;
7
use Illuminate\Auth\GuardHelpers;
8
use Illuminate\Contracts\Auth\Authenticatable;
9
use Illuminate\Contracts\Auth\Guard;
10
use Illuminate\Contracts\Auth\UserProvider;
11
use Illuminate\Support\Facades\Log;
12
use JWTAuth\Contracts\JwtBlockListContract;
13
use JWTAuth\Contracts\WithJwtToken;
14
use JWTAuth\Exceptions\JWTAuthException;
15
16
/**
17
 * Class JwtGuard
18
 * @package JWTAuth
19
 *
20
 * @property EloquentUserProvider $provider
21
 */
22
class JWTGuard implements Guard
23
{
24
    use GuardHelpers;
25
26
    /**
27
     * JWT token manager.
28
     *
29
     * @var JWTManager
30
     */
31
    protected JWTManager $jwt;
32
33
    /**
34
     * JWT black list manager.
35
     *
36
     * @var JwtBlockListContract
37
     */
38
    protected JwtBlockListContract $blockList;
39
40
    /**
41
     * The name of the query string item from the request containing the API token.
42
     *
43
     * @var string
44
     */
45
    protected string $inputKey;
46
47 4
    public function __construct(UserProvider $provider, JWTManager $jwt, JwtBlockListContract $blockList, array $options = [])
48
    {
49 4
        $this->jwt       = $jwt;
50 4
        $this->blockList = $blockList;
51 4
        $this->provider  = $provider;
52 4
        $this->inputKey  = $options['input_key'] ?? 'api_token';
53
    }
54
55
    /**
56
     * @return JwtBlockListContract
57
     */
58 2
    public function blockList(): JwtBlockListContract
59
    {
60 2
        return $this->blockList;
61
    }
62
63
64
    /**
65
     * @inheritDoc
66
     */
67 4
    public function user()
68
    {
69
        // If we've already retrieved the user for the current request we can just
70
        // return it back immediately. We do not want to fetch the user data on
71
        // every call to this method because that would be tremendously slow.
72 4
        if (!is_null($this->user)) {
73 4
            return $this->user;
74
        }
75
76 1
        $user = null;
77
78 1
        $jwtToken = $this->decodeToken($this->getTokenForRequest());
79
80
        if (
81 1
            $jwtToken &&
82 1
            $identifier = $jwtToken->payload()->get($this->provider->createModel()->getJwtPayloadIdentifierKey(), false)
83
        ) {
84
            /** @var WithJwtToken $user */
85 1
            $user = $this->provider->retrieveByCredentials([
86 1
                $this->provider->createModel()->getJwtAuthIdentifierKey() => $identifier,
87 1
            ]);
88 1
            if ($user && $user instanceof WithJwtToken) {
0 ignored issues
show
$user is always a sub-type of JWTAuth\Contracts\WithJwtToken.
Loading history...
89 1
                $user->withJwtToken($jwtToken);
90
            }
91
        }
92
93 1
        return $this->user = $user;
94
    }
95
96 1
    public function validate(array $credentials = [])
97
    {
98 1
        if (empty($credentials)) {
99 1
            return false;
100
        }
101
102 1
        if ($this->provider->retrieveByCredentials($credentials)) {
103 1
            return true;
104
        }
105
106 1
        return false;
107
    }
108
109
    /**
110
     * Get the token for the current request.
111
     *
112
     * @return string
113
     */
114 1
    public function getTokenForRequest()
115
    {
116 1
        $request = request();
117
118 1
        $token = $request->query($this->inputKey);
119
120 1
        if (empty($token)) {
121 1
            $token = $request->input($this->inputKey);
122
        }
123
124 1
        if (empty($token)) {
125 1
            $token = $request->bearerToken();
126
        }
127
128 1
        if (empty($token)) {
129 1
            $token = $request->getPassword();
130
        }
131
132 1
        return $token;
133
    }
134
135
    /**
136
     * Attempt to authenticate the user and return the token.
137
     *
138
     * @param  array  $credentials
139
     *
140
     * @return false|string
141
     * @throws JWTAuthException
142
     */
143 4
    public function attempt(array $credentials)
144
    {
145
146
        /** @var WithJwtToken|Authenticatable $user */
147 4
        if (!($user = $this->provider->retrieveByCredentials($credentials))) {
148
            return false;
149
        }
150
151 4
        if (isset($credentials['password'])) {
152 4
            if (!$this->provider->validateCredentials($user, $credentials)) {
153
                return false;
154
            }
155
        }
156
157 4
        if (!($user instanceof WithJwtToken)) {
158
            throw new JWTAuthException('User should implement "WithJwtToken"');
159
        }
160
161
        try {
162 4
            $token = $this->createTokenForUser($user);
163 4
            $this->setUser($user);
164
165 4
            return $token->encode();
166
        } catch (\Exception $e) {
167
            throw new JWTAuthException('Token creation error', 500, $e);
168
        }
169
    }
170
171 1
    public function logout()
172
    {
173
        /** @var WithJwtToken $user */
174 1
        $user = $this->user();
175
176 1
        if ($user && $user->currentJwtToken()) {
177 1
            $this->blockList->add($user->currentJwtToken());
178
        }
179
180
        // Once we have fired the logout event we will clear the users out of memory
181
        // so they are no longer available as the user is no longer considered as
182
        // being signed into this application and should not be available here.
183 1
        $this->unsetUser();
184
    }
185
186 4
    public function unsetUser(): static
187
    {
188 4
        $this->user = null;
189
190 4
        return $this;
191
    }
192
193 4
    public function getJWTManager(): JWTManager
194
    {
195 4
        return $this->jwt;
196
    }
197
198 1
    public function decodeToken(?string $token): ?Contracts\JWTManagerContract
199
    {
200 1
        if ($token) {
201
            try {
202 1
                $jwtToken = $this->jwt->decode($token);
203
                if (
204 1
                    !$this->blockList->isBlockListed($jwtToken) &&
205 1
                    $jwtToken->payload()->isValid()
206
                ) {
207 1
                    return $jwtToken;
208
                }
209
            } catch (\Exception $e) {
210
                // Token not valid
211
                Log::info($e->getMessage());
212
            }
213
        }
214
215 1
        return null;
216
    }
217
218 4
    public function createTokenForUser(WithJwtToken $user, string $name = 'jwt', array $abilities = ['*'], ?int $lifetimeInSeconds = null): Contracts\JWTManagerContract
219
    {
220 4
        $token = $this->jwt->setPayload($user->createPayload($name, $abilities, $lifetimeInSeconds));
221 4
        $user->withJwtToken($token);
222
223 4
        return $token;
224
    }
225
}
226