Passed
Pull Request — master (#36)
by
unknown
02:21
created

ApiAuth::verifyOtp()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 3
nop 2
dl 0
loc 25
rs 9.8333
c 0
b 0
f 0
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.0.0
13
 */
14
15
namespace Quantum\Libraries\Auth;
16
17
use Quantum\Exceptions\ExceptionMessages;
18
use Quantum\Exceptions\AuthException;
19
use Quantum\Libraries\JWToken\JWToken;
20
use Quantum\Libraries\Hasher\Hasher;
21
use Quantum\Http\Response;
22
use Quantum\Http\Request;
23
use Quantum\Libraries\Mailer\Mailer;
24
25
/**
26
 * Class ApiAuth
27
 * @package Quantum\Libraries\Auth
28
 */
29
class ApiAuth extends BaseAuth implements AuthenticableInterface
30
{
31
32
    /**
33
     * @var JWToken
34
     */
35
    protected $jwt;
36
37
    /**
38
     * @var Hasher
39
     */
40
    protected $hasher;
41
42
    /**
43
     * @var AuthServiceInterface
44
     */
45
    protected $authService;
46
47
    /**
48
     * @var array
49
     */
50
    protected $keys = [];
51
52
    /**
53
     * @var string
54
     */
55
    protected $authUserKey = 'auth_user';
56
57
    /**
58
     * ApiAuth constructor.
59
     * @param AuthServiceInterface $authService
60
     * @param Hasher $hasher
61
     * @param JWToken|null $jwt
62
     */
63
    public function __construct(AuthServiceInterface $authService, Hasher $hasher, JWToken $jwt = null)
64
    {
65
        $this->jwt = $jwt;
66
        $this->hasher = $hasher;
67
        $this->authService = $authService;
68
        $this->keys = $this->authService->getDefinedKeys();
69
    }
70
71
    /**
72
     * Sign In
73
     * @param string $username
74
     * @param string $password
75
     * @return string|array
76
     * @throws AuthException
77
     */
78
    public function signin($mailer, $username, $password)
79
    {
80
        $user = $this->authService->get($this->keys[self::USERNAME_KEY], $username);
81
82
        if (empty($user)) {
83
            throw new AuthException(ExceptionMessages::INCORRECT_AUTH_CREDENTIALS);
84
        }
85
86
        if (!$this->hasher->check($password, $user[$this->keys[self::PASSWORD_KEY]])) {
87
            throw new AuthException(ExceptionMessages::INCORRECT_AUTH_CREDENTIALS);
88
        }
89
        
90
        if (!$this->isActivated($user)) {
91
            throw new AuthException(ExceptionMessages::INACTIVE_ACCOUNT);
92
        }
93
94
        if (filter_var(config()->get('2SV'), FILTER_VALIDATE_BOOLEAN)) {
95
            $otpToken = $this->twoStepVerification($mailer, $user);
96
            return $otpToken;
97
98
        } else {
99
            $tokens = $this->setUpdatedTokens($user);
100
            return $tokens;
101
        }
102
    }
103
104
    /**
105
     * Sign Out
106
     * @return bool|mixed
107
     */
108
    public function signout()
109
    {
110
        $refreshToken = Request::getHeader($this->keys[self::REFRESH_TOKEN_KEY]);
111
112
        $user = $this->authService->get($this->keys[self::REFRESH_TOKEN_KEY], $refreshToken);
113
114
        if (!empty($user)) {
115
            $this->authService->update(
116
                    $this->keys[self::REFRESH_TOKEN_KEY],
117
                    $refreshToken,
118
                    [
119
                        $this->authUserKey => $user,
120
                        $this->keys[self::REFRESH_TOKEN_KEY] => ''
121
                    ]
122
            );
123
124
            Request::deleteHeader($this->keys[self::REFRESH_TOKEN_KEY]);
125
            Request::deleteHeader('Authorization');
126
            Response::delete('tokens');
127
128
            return true;
129
        }
130
131
        return false;
132
    }
133
134
    /**
135
     * User
136
     * @return object|null
137
     */
138
    public function user()
139
    {
140
        try {
141
            $accessToken = base64_decode(Request::getAuthorizationBearer());
142
            return (object) $this->jwt->retrieve($accessToken)->fetchData();
143
        } catch (\Exception $e) {
144
            if (Request::hasHeader($this->keys[self::REFRESH_TOKEN_KEY])) {
145
                $user = $this->checkRefreshToken();
146
                
147
                if ($user) {
148
                    $this->setUpdatedTokens($user);
149
                    return $this->user();
150
                }
151
            }
152
            return null;
153
        }
154
    }
155
156
    /**
157
     * Get Updated Tokens
158
     * @param array $user
159
     * @return array
160
     */
161
    public function getUpdatedTokens(array $user)
162
    {
163
        return [
164
            $this->keys[self::REFRESH_TOKEN_KEY] => $this->generateToken(),
165
            $this->keys[self::ACCESS_TOKEN_KEY] => base64_encode($this->jwt->setData($this->filterFields($user))->compose())
166
        ];
167
    }
168
169
    /**
170
     * Verify OTP
171
     * @param integer $otp
172
     * @param string $otpToken
173
     * @return array
174
     * @throws AuthException
175
     */
176
    public function verifyOtp($otp, $otpToken)
177
    {
178
        $user = $this->authService->get($this->keys[self::OTP_TOKEN_KEY], $otpToken);
179
180
        if (empty($user) || $otp != $user[$this->keys[self::OTP_KEY]]) {
181
            throw new AuthException(ExceptionMessages::INCORRECT_VERIFICATION_CODE);
182
        }
183
        
184
        if (new \DateTime() >= new \DateTime($user[$this->keys[self::OTP_EXPIRY_KEY]])){
185
            throw new AuthException(ExceptionMessages::VERIFICATION_CODE_EXPIRED);
186
        }
187
188
        $this->authService->update(
189
                $this->keys[self::USERNAME_KEY], 
190
                $user[$this->keys[self::USERNAME_KEY]], 
191
                [
192
                    $this->keys[self::OTP_KEY] => null,
193
                    $this->keys[self::OTP_EXPIRY_KEY] => null,
194
                    $this->keys[self::OTP_TOKEN_KEY] => null,
195
                ]
196
        );
197
198
        $tokens = $this->setUpdatedTokens($this->filterFields($user));
199
200
        return $tokens;
201
    }
202
203
    /**
204
     * Resend OTP
205
     * @param Mailer $mailer
206
     * @param string $otpToken
207
     * @return string
208
     * @throws AuthException
209
     */
210
211
    public function resendOtp(Mailer $mailer, $otpToken)
212
    {
213
        $user = $this->authService->get($this->keys[self::OTP_TOKEN_KEY], $otpToken);
214
215
        if (empty($user)) {
216
            throw new AuthException(ExceptionMessages::INCORRECT_AUTH_CREDENTIALS);
217
        }
218
219
        return $this->twoStepVerification($mailer, $user);
220
    }
221
222
    /**
223
     * Check Refresh Token
224
     * @return bool|mixed
225
     */
226
    protected function checkRefreshToken()
227
    {
228
        $user = $this->authService->get($this->keys[self::REFRESH_TOKEN_KEY], Request::getHeader($this->keys[self::REFRESH_TOKEN_KEY]));
229
230
        if (!empty($user)) {
231
            return $user;
232
        }
233
234
        return false;
235
    }
236
237
    /**
238
     * Set Updated Tokens
239
     * @param array $user
240
     * @return array
241
     */
242
    protected function setUpdatedTokens(array $user)
243
    {
244
        $tokens = $this->getUpdatedTokens($user);
245
246
        $this->authService->update(
247
                $this->keys[self::USERNAME_KEY],
248
                $user[$this->keys[self::USERNAME_KEY]],
249
                [
250
                    $this->authUserKey => $user,
251
                    $this->keys[self::REFRESH_TOKEN_KEY] => $tokens[$this->keys[self::REFRESH_TOKEN_KEY]]
252
                ]
253
        );
254
255
        Request::setHeader($this->keys[self::REFRESH_TOKEN_KEY], $tokens[$this->keys[self::REFRESH_TOKEN_KEY]]);
256
        Request::setHeader('Authorization', 'Bearer ' . $tokens[$this->keys[self::ACCESS_TOKEN_KEY]]);
257
        Response::set('tokens', $tokens);
258
259
        return $tokens;
260
    }
261
262
}
263