OAuthClient   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 96
dl 0
loc 181
rs 10
c 0
b 0
f 0
wmc 25

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getAccessToken() 0 13 3
A __construct() 0 16 1
A fetchInitialTokens() 0 20 4
B refreshAccessToken() 0 51 8
A storeTokens() 0 4 1
A request() 0 23 6
A parseAndStoreTokens() 0 11 2
1
<?php
2
3
namespace Sarahman\OauthTokensClient;
4
5
use Exception;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\Exception\RequestException;
8
use Illuminate\Cache\Repository as CacheRepository;
9
use Sarahman\HttpRequestApiLog\Traits\WritesHttpLogs;
10
11
class OAuthClient
12
{
13
    use WritesHttpLogs;
14
15
    private static $accessTokenKey;
16
    private static $refreshTokenKey;
17
    private static $lockKey;
18
19
    /** @var Client */
20
    private $httpClient;
21
22
    /** @var CacheRepository */
23
    private $cache;
24
25
    private $tokenUrl;
26
    private $refreshUrl;
27
    private $grantType;
28
    private $clientId;
29
    private $clientSecret;
30
    private $username;
31
    private $password;
32
    private $scope = '';
33
34
    public function __construct(Client $httpClient, CacheRepository $cache, array $oauthConfig, array $tokenPrefixes, $lockKey)
35
    {
36
        $this->httpClient = $httpClient;
37
        $this->cache = $cache;
38
        $this->tokenUrl = $oauthConfig['TOKEN_URL'];
39
        $this->refreshUrl = $oauthConfig['REFRESH_URL'];
40
        $this->grantType = $oauthConfig['GRANT_TYPE'];
41
        $this->clientId = $oauthConfig['CLIENT_ID'];
42
        $this->clientSecret = $oauthConfig['CLIENT_SECRET'];
43
        $this->username = $oauthConfig['USERNAME'];
44
        $this->password = $oauthConfig['PASSWORD'];
45
        $this->scope = $oauthConfig['SCOPE'];
46
47
        self::$accessTokenKey = $tokenPrefixes['ACCESS'];
48
        self::$refreshTokenKey = $tokenPrefixes['REFRESH'];
49
        self::$lockKey = $lockKey;
50
    }
51
52
    public function request($method, $uri, array $options = array(), $retryCount = 1)
53
    {
54
        isset($options['headers']) || $options['headers'] = array();
55
        $options['headers']['Authorization'] = "Bearer {$this->getAccessToken()}";
56
57
        try {
58
            $response = $this->httpClient->request($method, $uri, $options);
59
60
            $this->log($method, $uri, $options, new GuzzleResponse(200, [], $response->getBody()));
61
62
            return $response;
63
        } catch (RequestException $e) {
64
            $response = $e->getResponse();
65
66
            if ($response && $response->getStatusCode() === 401 && $retryCount > 0) {
67
                $options['headers']['Authorization'] = "Bearer {$this->refreshAccessToken()}";
68
69
                return $this->request($method, $uri, $options, $retryCount - 1);
70
            }
71
72
            $this->log($method, $uri, $options, new GuzzleResponse($e->getCode(), [], $response->getBody()));
73
74
            throw $e;
75
        }
76
    }
77
78
    private function getAccessToken()
79
    {
80
        $token = $this->cache->get(self::$accessTokenKey);
81
82
        if ($token) {
83
            return $token;
84
        }
85
86
        while ($this->cache->has(self::$lockKey)) {
87
            usleep(50000); // wait 50ms
88
        }
89
90
        return $this->refreshAccessToken();
91
    }
92
93
    private function refreshAccessToken()
94
    {
95
        if ($this->cache->has(self::$lockKey)) {
96
            while ($this->cache->has(self::$lockKey)) {
97
                usleep(50000);
98
            }
99
100
            return $this->cache->get(self::$accessTokenKey);
101
        }
102
103
        $this->cache->put(self::$lockKey, true, 10);
104
105
        try {
106
            $token = '';
107
            $refreshToken = $this->cache->get(self::$refreshTokenKey);
108
109
            if (!$refreshToken) {
110
                $token = $this->fetchInitialTokens();
111
            } else {
112
                $response = $this->httpClient->post($this->refreshUrl, $options = array(
113
                    'form_params' => array(
114
                        'grant_type'    => 'refresh_token',
115
                        'refresh_token' => $refreshToken,
116
                        'client_id'     => $this->clientId,
117
                        'client_secret' => $this->clientSecret,
118
                        'scope'         => $this->scope,
119
                    ),
120
                ));
121
122
                $this->log('POST', $this->refreshUrl, $options, new GuzzleResponse(200, [], $response->getBody()));
123
124
                $token = $this->parseAndStoreTokens($response);
125
            }
126
        } catch (RequestException $e) {
127
            $response = $e->getResponse();
128
129
            $this->log('POST', $this->refreshUrl, $options, new GuzzleResponse($e->getCode(), [], $response->getBody()));
130
131
            if ($response && $response->getStatusCode() === 401) {
132
                return $this->fetchInitialTokens();
133
            }
134
135
            throw $e;
136
        } catch (Exception $e) {
137
            $this->cache->forget(self::$lockKey);
138
            throw $e;
139
        }
140
141
        $this->cache->forget(self::$lockKey);
142
143
        return $token;
144
    }
145
146
    private function fetchInitialTokens()
147
    {
148
        $params = array(
149
            'grant_type' => $this->grantType,
150
            'client_id'  => $this->clientId,
151
            'client_secret' => $this->clientSecret,
152
        );
153
154
        if ('password' === $this->grantType && $this->username && $this->password) {
155
            $params['username'] = $this->username;
156
            $params['password'] = $this->password;
157
        }
158
159
        $response = $this->httpClient->post($uri = $this->tokenUrl, $options = array(
160
            'form_params' => $params,
161
        ));
162
163
        $this->log('POST', $this->tokenUrl, $options, new GuzzleResponse(200, [], $response->getBody()));
164
165
        return $this->parseAndStoreTokens($response);
166
    }
167
168
    /**
169
     * Parses the OAuth token response and stores the tokens.
170
     *
171
     * @param \GuzzleHttp\Message\ResponseInterface|\Psr\Http\Message\ResponseInterface $response The response containing OAuth tokens to be parsed and stored.
0 ignored issues
show
Bug introduced by
The type GuzzleHttp\Message\ResponseInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
172
     *
173
     * @return string
174
     */
175
    private function parseAndStoreTokens($response)
176
    {
177
        $data = json_decode((string) $response->getBody(), true);
178
179
        if (json_last_error()) {
180
            return '';
181
        }
182
183
        $this->storeTokens($data);
184
185
        return $data['access_token'];
186
    }
187
188
    private function storeTokens(array $data)
189
    {
190
        $this->cache->put(self::$accessTokenKey, $data['access_token'], $data['expires_in'] - 30);
191
        $this->cache->forever(self::$refreshTokenKey, $data['refresh_token']);
192
    }
193
}
194