Completed
Push — 2.0 ( afe19a...325c55 )
by Kirill
03:06
created

TokenAuth::resolveExistingUser()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 3
nop 1
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of laravel.su package.
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
declare(strict_types=1);
8
9
namespace App\Services;
10
11
use App\Models\User;
12
use Carbon\Carbon;
13
use Illuminate\Support\Arr;
14
use Illuminate\Contracts\Auth\Guard;
15
use Tymon\JWTAuth\Exceptions\JWTException;
16
use Tymon\JWTAuth\Providers\JWT\JWTInterface;
17
use Illuminate\Contracts\Auth\Authenticatable;
18
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
19
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
20
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
21
22
/**
23
 * Class TokenAuth
24
 * @package App\Services
25
 */
26
class TokenAuth
27
{
28
    /**
29
     * @var JWTInterface
30
     */
31
    private $jwt;
32
33
    /**
34
     * @var Guard
35
     */
36
    private $guard;
37
38
    /**
39
     * TokenAuth constructor.
40
     * @param JWTInterface $jwt
41
     * @param Guard $guard
42
     */
43
    public function __construct(JWTInterface $jwt, Guard $guard)
44
    {
45
        $this->jwt = $jwt;
46
        $this->guard = $guard;
47
    }
48
49
    /**
50
     * @param string $email
51
     * @param string $password
52
     * @return Authenticatable
53
     */
54
    public function attemptFromEmailAndPassword(string $email, string $password): ?Authenticatable
55
    {
56
        if (! $this->guard->validate(['email' => $email, 'password' => $password])) {
57
            return null;
58
        }
59
60
        return User::whereEmail($email)->first();
61
    }
62
63
    /**
64
     * @param int $id
65
     * @param string $password
66
     * @return Authenticatable
67
     */
68
    public function resolveFromIdAndPassword(int $id, string $password): ?Authenticatable
69
    {
70
        if (! $this->guard->validate(['id' => $id, 'password' => $password])) {
71
            return null;
72
        }
73
74
        return User::find($id);
75
    }
76
77
    /**
78
     * @param Guard $guard
79
     * @return string
80
     */
81
    public function fromGuard(Guard $guard): string
82
    {
83
        return $this->fromUser($guard->check() ? $guard->user() : $this->guest());
84
    }
85
86
    /**
87
     * @param Authenticatable $user
88
     * @return string
89
     */
90
    public function fromUser(Authenticatable $user): string
91
    {
92
        return $this->encode([
93
            'user'    => [
94
                'id'       => $user->getAuthIdentifier(),
95
                'password' => $user->getAuthPassword(),
96
            ],
97
            'created' => Carbon::now()->toRfc3339String(),
98
            'guest'   => 0 === (int)$user->getAuthIdentifier(),
99
            'token'   => $user->getRememberToken(),
100
        ]);
101
    }
102
103
    /**
104
     * @param array $payload
105
     * @return string
106
     */
107
    public function encode(array $payload): string
108
    {
109
        return $this->jwt->encode($payload);
110
    }
111
112
    /**
113
     * @return Authenticatable
114
     */
115
    public function guest(): Authenticatable
116
    {
117
        return new User(['id' => 0, 'name' => 'Guest']);
118
    }
119
120
    /**
121
     * @param string $token
122
     * @return Authenticatable
123
     * @throws BadRequestHttpException
124
     * @throws UnprocessableEntityHttpException
125
     */
126
    public function fromToken(string $token): Authenticatable
127
    {
128
        try {
129
            $userInfo = $this->decode($token);
130
            $this->verifyTokenCreated($userInfo);
131
132
        } catch (TokenExpiredException $e) {
133
            throw new BadRequestHttpException('Token lifetime is timed out.');
134
135
        } catch (JWTException $invalidException) {
136
            throw new BadRequestHttpException('Broken api token.');
137
        }
138
139
        if (false !== Arr::get($userInfo, 'guest', true)) {
140
            return $this->resolveExistingUser($userInfo);
141
        }
142
143
        return $this->guest();
144
    }
145
146
    /**
147
     * @param array $userInfo
148
     * @throws TokenExpiredException
149
     */
150
    private function verifyTokenCreated(array $userInfo): void
151
    {
152
        $created = Carbon::parse($userInfo['created'] ?? '0001-00-00 00:00');
153
154
        if (Carbon::now()->subMinutes(config('jwt.ttl')) > $created) {
155
            throw new TokenExpiredException();
156
        }
157
    }
158
159
    /**
160
     * @param string $token
161
     * @return array
162
     */
163
    public function decode(string $token): array
164
    {
165
        return $this->jwt->decode($token);
166
    }
167
168
    /**
169
     * @param array $userInfo
170
     * @return mixed
171
     * @throws UnprocessableEntityHttpException
172
     */
173
    private function resolveExistingUser(array $userInfo)
174
    {
175
        [$id, $password, $token] = [
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $password does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $token does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
176
            (int)Arr::get($userInfo, 'user.id'),
177
            (string)Arr::get($userInfo, 'user.password'),
178
            (string)Arr::get($userInfo, 'token'),
179
        ];
180
181
        $user = User::where('id', $id)->where('password', $password)->first();
182
183
        if ($user->remember_token !== $token) {
184
            throw new UnprocessableEntityHttpException('Invalid remember token');
185
        }
186
187
        if (! $user) {
188
            throw new UnprocessableEntityHttpException('Invalid user credentials.');
189
        }
190
191
        return $user;
192
    }
193
}