Passed
Pull Request — master (#182)
by Arman
03:14
created

ApiAdapter   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Importance

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