Passed
Pull Request — master (#182)
by Arman
05:01 queued 02:11
created

ApiAdapter   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 176
rs 10
c 0
b 0
f 0
wmc 15

9 Methods

Rating   Name   Duplication   Size   Complexity  
A checkRefreshToken() 0 5 1
A signin() 0 8 2
A getUpdatedTokens() 0 5 1
A setUpdatedTokens() 0 15 1
A user() 0 16 4
A verifyOtp() 0 4 1
A getInstance() 0 7 2
A signout() 0 21 2
A __construct() 0 10 1
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.5
13
 */
14
15
namespace Quantum\Libraries\Auth\Adapters;
16
17
use Quantum\Libraries\Auth\AuthenticatableInterface;
18
use Quantum\Libraries\Auth\AuthServiceInterface;
19
use Quantum\Libraries\Mailer\MailerInterface;
20
use Quantum\Libraries\JWToken\JWTException;
21
use Quantum\Libraries\Auth\AuthException;
22
use Quantum\Libraries\JWToken\JWToken;
23
use Quantum\Libraries\Auth\BaseAuth;
24
use Quantum\Libraries\Hasher\Hasher;
25
use Quantum\Libraries\Auth\User;
26
use Quantum\Http\Response;
27
use Quantum\Http\Request;
28
use Exception;
29
30
/**
31
 * Class ApiAuth
32
 * @package Quantum\Libraries\Auth
33
 */
34
class ApiAdapter extends BaseAuth implements AuthenticatableInterface
35
{
36
37
    /**
38
     * @var ApiAdapter
39
     */
40
    private static $instance;
41
42
    /**
43
     * @param AuthServiceInterface $authService
44
     * @param MailerInterface $mailer
45
     * @param Hasher $hasher
46
     * @param JWToken|null $jwt
47
     * @throws AuthException
48
     */
49
    private function __construct(AuthServiceInterface $authService, MailerInterface $mailer, Hasher $hasher, JWToken $jwt = null)
50
    {
51
        $this->authService = $authService;
52
        $this->mailer = $mailer;
53
        $this->hasher = $hasher;
54
        $this->jwt = $jwt;
55
56
        $userSchema = $this->authService->userSchema();
57
58
        $this->verifySchema($userSchema);
59
    }
60
61
    /**
62
     * @param AuthServiceInterface $authService
63
     * @param MailerInterface $mailer
64
     * @param Hasher $hasher
65
     * @param JWToken|null $jwt
66
     * @return self
67
     * @throws AuthException
68
     */
69
    public static function getInstance(AuthServiceInterface $authService, MailerInterface $mailer, Hasher $hasher, JWToken $jwt = null): self
70
    {
71
        if (self::$instance === null) {
72
            self::$instance = new self($authService, $mailer, $hasher, $jwt);
73
        }
74
75
        return self::$instance;
76
    }
77
78
    /**
79
     * Sign In
80
     * @param string $username
81
     * @param string $password
82
     * @return array|string
83
     * @throws AuthException
84
     * @throws JWTException
85
     * @throws Exception
86
     */
87
    public function signin(string $username, string $password)
88
    {
89
        $user = $this->getUser($username, $password);
90
91
        if (filter_var(config()->get('2FA'), FILTER_VALIDATE_BOOLEAN)) {
92
            return $this->twoStepVerification($user);
93
        } else {
94
            return $this->setUpdatedTokens($user);
95
        }
96
    }
97
98
    /**
99
     * Sign Out
100
     * @return bool
101
     */
102
    public function signout(): bool
103
    {
104
        $refreshToken = Request::getHeader($this->keyFields[self::REFRESH_TOKEN_KEY]);
105
106
        $user = $this->authService->get($this->keyFields[self::REFRESH_TOKEN_KEY], $refreshToken);
107
108
        if ($user) {
109
            $this->authService->update(
110
                $this->keyFields[self::REFRESH_TOKEN_KEY],
111
                $refreshToken,
112
                array_merge($this->getVisibleFields($user), [$this->keyFields[self::REFRESH_TOKEN_KEY] => ''])
113
            );
114
115
            Request::deleteHeader($this->keyFields[self::REFRESH_TOKEN_KEY]);
116
            Request::deleteHeader('Authorization');
117
            Response::delete('tokens');
118
119
            return true;
120
        }
121
122
        return false;
123
    }
124
125
    /**
126
     * User
127
     * @return User|null
128
     * @throws JWTException
129
     */
130
    public function user(): ?User
131
    {
132
        try {
133
            $accessToken = base64_decode((string)Request::getAuthorizationBearer());
134
            return (new User())->setData($this->jwt->retrieve($accessToken)->fetchData());
135
        } catch (Exception $e) {
136
            if (Request::hasHeader($this->keyFields[self::REFRESH_TOKEN_KEY])) {
137
                $user = $this->checkRefreshToken();
138
139
                if ($user) {
140
                    $this->setUpdatedTokens($user);
141
                    return $this->user();
142
                }
143
            }
144
145
            return null;
146
        }
147
    }
148
149
    /**
150
     * Get Updated Tokens
151
     * @param User $user
152
     * @return array
153
     * @throws JWTException
154
     */
155
    public function getUpdatedTokens(User $user): array
156
    {
157
        return [
158
            $this->keyFields[self::REFRESH_TOKEN_KEY] => $this->generateToken(),
159
            $this->keyFields[self::ACCESS_TOKEN_KEY] => base64_encode($this->jwt->setData($this->getVisibleFields($user))->compose())
160
        ];
161
    }
162
163
    /**
164
     * Verify OTP
165
     * @param int $otp
166
     * @param string $otpToken
167
     * @return array
168
     * @throws AuthException
169
     * @throws JWTException
170
     */
171
    public function verifyOtp(int $otp, string $otpToken): array
172
    {
173
        $user = $this->verifyAndUpdateOtp($otp, $otpToken);
174
        return $this->setUpdatedTokens($user);
175
    }
176
177
    /**
178
     * Check Refresh Token
179
     * @return User|null
180
     */
181
    protected function checkRefreshToken(): ?User
182
    {
183
        return $this->authService->get(
184
            $this->keyFields[self::REFRESH_TOKEN_KEY],
185
            Request::getHeader($this->keyFields[self::REFRESH_TOKEN_KEY])
186
        );
187
    }
188
189
    /**
190
     * Set Updated Tokens
191
     * @param User $user
192
     * @return array
193
     * @throws JWTException
194
     */
195
    protected function setUpdatedTokens(User $user): array
196
    {
197
        $tokens = $this->getUpdatedTokens($user);
198
199
        $this->authService->update(
200
            $this->keyFields[self::USERNAME_KEY],
201
            $user->getFieldValue($this->keyFields[self::USERNAME_KEY]),
202
            array_merge($this->getVisibleFields($user), [$this->keyFields[self::REFRESH_TOKEN_KEY] => $tokens[$this->keyFields[self::REFRESH_TOKEN_KEY]]])
203
        );
204
205
        Request::setHeader($this->keyFields[self::REFRESH_TOKEN_KEY], $tokens[$this->keyFields[self::REFRESH_TOKEN_KEY]]);
206
        Request::setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[self::ACCESS_TOKEN_KEY]]);
207
        Response::set('tokens', $tokens);
208
209
        return $tokens;
210
    }
211
212
}
213