Passed
Pull Request — master (#222)
by Arman
02:52
created

JwtAuthAdapter::verifyOtp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 4
rs 10
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.6
13
 */
14
15
namespace Quantum\Libraries\Auth\Adapters;
16
17
use Quantum\Libraries\Auth\Contracts\AuthenticatableInterface;
18
use Quantum\Libraries\Auth\Contracts\AuthServiceInterface;
19
use Quantum\Libraries\Auth\Exceptions\AuthException;
20
use Quantum\Libraries\Jwt\Exceptions\JwtException;
21
use Quantum\Libraries\Auth\Constants\AuthKeys;
22
use Quantum\Libraries\Auth\Traits\AuthTrait;
23
use Quantum\Libraries\Mailer\Mailer;
24
use Quantum\Libraries\Hasher\Hasher;
25
use Quantum\Libraries\Jwt\JwtToken;
26
use Quantum\Libraries\Auth\User;
27
use Quantum\Http\Response;
28
use Quantum\Http\Request;
29
use Exception;
30
31
/**
32
 * Class ApiAuth
33
 * @package Quantum\Libraries\Auth
34
 */
35
class JwtAuthAdapter implements AuthenticatableInterface
36
{
37
38
    use AuthTrait;
39
40
    /**
41
     * @param AuthServiceInterface $authService
42
     * @param Mailer $mailer
43
     * @param Hasher $hasher
44
     * @param JwtToken|null $jwt
45
     * @throws AuthException
46
     */
47
    public function __construct(AuthServiceInterface $authService, Mailer $mailer, Hasher $hasher, JwtToken $jwt = null)
48
    {
49
        $this->authService = $authService;
50
        $this->mailer = $mailer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $mailer of type Quantum\Libraries\Mailer\Mailer is incompatible with the declared type Quantum\Libraries\Mailer\Contracts\MailerInterface of property $mailer.

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...
51
        $this->hasher = $hasher;
52
        $this->jwt = $jwt;
53
54
        $this->verifySchema($this->authService->userSchema());
55
    }
56
57
    /**
58
     * @inheritDoc
59
     * @throws AuthException
60
     * @throws JwtException
61
     * @throws Exception
62
     */
63
    public function signin(string $username, string $password)
64
    {
65
        $user = $this->getUser($username, $password);
66
67
        if ($this->isTwoFactorEnabled()) {
68
            return $this->twoStepVerification($user);
69
        }
70
71
        return $this->setUpdatedTokens($user);
72
    }
73
74
    /**
75
     * @inheritDoc
76
     */
77
    public function signout(): bool
78
    {
79
        $refreshToken = Request::getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]);
80
81
        $user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN], $refreshToken);
82
83
        if ($user) {
84
            $this->authService->update(
85
                $this->keyFields[AuthKeys::REFRESH_TOKEN],
86
                $refreshToken,
87
                array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => ''])
88
            );
89
90
            Request::deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]);
91
            Request::deleteHeader('Authorization');
92
            Response::delete('tokens');
93
94
            return true;
95
        }
96
97
        return false;
98
    }
99
100
    /**
101
     * @inheritDoc
102
     * @throws JwtException
103
     */
104
    public function user(): ?User
105
    {
106
        try {
107
            return $this->getUserFromAccessToken();
108
        } catch (Exception $e) {
109
            return $this->getUserFromRefreshToken();
110
        }
111
    }
112
113
    /**
114
     * Verify OTP
115
     * @param int $otp
116
     * @param string $otpToken
117
     * @return array
118
     * @throws AuthException
119
     * @throws JwtException
120
     */
121
    public function verifyOtp(int $otp, string $otpToken): array
122
    {
123
        $user = $this->verifyAndUpdateOtp($otp, $otpToken);
124
        return $this->setUpdatedTokens($user);
125
    }
126
127
    /**
128
     * Refresh user data
129
     * @return bool
130
     * @throws JwtException
131
     */
132
    public function refreshUser(): bool
133
    {
134
        $refreshToken = Request::getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]);
135
136
        $user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN], $refreshToken);
137
138
        if($user) {
139
            $this->setUpdatedTokens($user);
140
            return true;
141
        }
142
143
        return false;
144
    }
145
146
    /**
147
     * Get Updated Tokens
148
     * @param User $user
149
     * @return array
150
     * @throws JwtException
151
     */
152
    protected function getUpdatedTokens(User $user): array
153
    {
154
        return [
155
            $this->keyFields[AuthKeys::REFRESH_TOKEN] => $this->generateToken(),
156
            $this->keyFields[AuthKeys::ACCESS_TOKEN] => base64_encode($this->jwt->setData($this->getVisibleFields($user))->compose())
157
        ];
158
    }
159
160
    /**
161
     * Set Updated Tokens
162
     * @param User $user
163
     * @return array
164
     * @throws JwtException
165
     */
166
    protected function setUpdatedTokens(User $user): array
167
    {
168
        $tokens = $this->getUpdatedTokens($user);
169
170
        $this->authService->update(
171
            $this->keyFields[AuthKeys::USERNAME],
172
            $user->getFieldValue($this->keyFields[AuthKeys::USERNAME]),
173
            array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]])
174
        );
175
176
        Request::setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]);
177
        Request::setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN]]);
178
        Response::set('tokens', $tokens);
179
180
        return $tokens;
181
    }
182
183
    /**
184
     * Check Refresh Token
185
     * @return User|null
186
     */
187
    protected function checkRefreshToken(): ?User
188
    {
189
        return $this->authService->get(
190
            $this->keyFields[AuthKeys::REFRESH_TOKEN],
191
            Request::getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])
192
        );
193
    }
194
195
    /**
196
     * @return User|null
197
     */
198
    private function getUserFromAccessToken(): ?User
199
    {
200
        $authorizationBearer = Request::getAuthorizationBearer();
201
202
        if (!$authorizationBearer) {
203
            return null;
204
        }
205
206
        $accessToken = base64_decode($authorizationBearer);
207
208
        $userData = $this->jwt->retrieve($accessToken)->fetchData();
209
210
        return $userData ? (new User())->setData($userData) : null;
211
    }
212
213
    /**
214
     * @return User|null
215
     * @throws JwtException
216
     */
217
    private function getUserFromRefreshToken(): ?User
218
    {
219
        if (!Request::hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])) {
220
            return null;
221
        }
222
223
        $user = $this->checkRefreshToken();
224
225
        if ($user) {
226
            $this->setUpdatedTokens($user);
227
            return $this->user();
228
        }
229
230
        return null;
231
    }
232
}