Issues (5)

src/OAuthClient.php (1 issue)

Labels
Severity
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
10
class OAuthClient
11
{
12
    private static $accessTokenKey;
13
    private static $refreshTokenKey;
14
    private static $lockKey;
15
16
    /** @var Client */
17
    private $httpClient;
18
19
    /** @var CacheRepository */
20
    private $cache;
21
22
    private $tokenUrl;
23
    private $refreshUrl;
24
    private $grantType;
25
    private $clientId;
26
    private $clientSecret;
27
    private $username;
28
    private $password;
29
    private $scope = '';
30
31
    public function __construct(Client $httpClient, CacheRepository $cache, array $oauthConfig, array $tokenPrefixes, $lockKey)
32
    {
33
        $this->httpClient = $httpClient;
34
        $this->cache = $cache;
35
        $this->tokenUrl = $oauthConfig['TOKEN_URL'];
36
        $this->refreshUrl = $oauthConfig['REFRESH_URL'];
37
        $this->grantType = $oauthConfig['GRANT_TYPE'];
38
        $this->clientId = $oauthConfig['CLIENT_ID'];
39
        $this->clientSecret = $oauthConfig['CLIENT_SECRET'];
40
        $this->username = $oauthConfig['USERNAME'];
41
        $this->password = $oauthConfig['PASSWORD'];
42
        $this->scope = $oauthConfig['SCOPE'];
43
44
        self::$accessTokenKey = $tokenPrefixes['ACCESS'];
45
        self::$refreshTokenKey = $tokenPrefixes['REFRESH'];
46
        self::$lockKey = $lockKey;
47
    }
48
49
    public function request($method, $uri, array $options = array(), $retryCount = 1)
50
    {
51
        isset($options['headers']) || $options['headers'] = array();
52
        $options['headers']['Authorization'] = "Bearer {$this->getAccessToken()}";
53
54
        try {
55
            return $this->httpClient->request($method, $uri, $options);
56
        } catch (RequestException $e) {
57
            $response = $e->getResponse();
58
59
            if ($response && $response->getStatusCode() === 401 && $retryCount > 0) {
60
                $options['headers']['Authorization'] = "Bearer {$this->refreshAccessToken()}";
61
62
                return $this->request($method, $uri, $options, $retryCount - 1);
63
            }
64
65
            throw $e;
66
        }
67
    }
68
69
    private function getAccessToken()
70
    {
71
        $token = $this->cache->get(self::$accessTokenKey);
72
73
        if ($token) {
74
            return $token;
75
        }
76
77
        while ($this->cache->has(self::$lockKey)) {
78
            usleep(50000); // wait 50ms
79
        }
80
81
        return $this->refreshAccessToken();
82
    }
83
84
    private function refreshAccessToken()
85
    {
86
        if ($this->cache->has(self::$lockKey)) {
87
            while ($this->cache->has(self::$lockKey)) {
88
                usleep(50000);
89
            }
90
91
            return $this->cache->get(self::$accessTokenKey);
92
        }
93
94
        $this->cache->put(self::$lockKey, true, 10);
95
96
        try {
97
            $token = '';
98
            $refreshToken = $this->cache->get(self::$refreshTokenKey);
99
100
            if (!$refreshToken) {
101
                $token = $this->fetchInitialTokens();
102
            } else {
103
                $response = $this->httpClient->post($this->refreshUrl, array(
104
                    'form_params' => array(
105
                        'grant_type'    => 'refresh_token',
106
                        'refresh_token' => $refreshToken,
107
                        'client_id'     => $this->clientId,
108
                        'client_secret' => $this->clientSecret,
109
                        'scope'         => $this->scope,
110
                    ),
111
                ));
112
113
                $token = $this->parseAndStoreTokens($response);
114
            }
115
        } catch (RequestException $e) {
116
            $response = $e->getResponse();
117
118
            if ($response && $response->getStatusCode() === 401) {
119
                return $this->fetchInitialTokens();
120
            }
121
122
            throw $e;
123
        } catch (Exception $e) {
124
            $this->cache->forget(self::$lockKey);
125
            throw $e;
126
        }
127
128
        $this->cache->forget(self::$lockKey);
129
130
        return $token;
131
    }
132
133
    private function fetchInitialTokens()
134
    {
135
        $params = array(
136
            'grant_type' => $this->grantType,
137
            'client_id'  => $this->clientId,
138
            'client_secret' => $this->clientSecret,
139
        );
140
141
        if ('password' === $this->grantType && $this->username && $this->password) {
142
            $params['username'] = $this->username;
143
            $params['password'] = $this->password;
144
        }
145
146
        $response = $this->httpClient->post($this->tokenUrl, array(
147
            'form_params' => $params,
148
        ));
149
150
        return $this->parseAndStoreTokens($response);
151
    }
152
153
    /**
154
     * Parses the OAuth token response and stores the tokens.
155
     *
156
     * @param \GuzzleHttp\Message\ResponseInterface|\Psr\Http\Message\ResponseInterface $response
0 ignored issues
show
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...
157
     *
158
     * @return string
159
     */
160
    private function parseAndStoreTokens($response)
161
    {
162
        $data = json_decode((string) $response->getBody(), true);
163
164
        if (json_last_error()) {
165
            return '';
166
        }
167
168
        $this->storeTokens($data);
169
170
        return $data['access_token'];
171
    }
172
173
    private function storeTokens(array $data)
174
    {
175
        $this->cache->put(self::$accessTokenKey, $data['access_token'], $data['expires_in'] - 30);
176
        $this->cache->forever(self::$refreshTokenKey, $data['refresh_token']);
177
    }
178
}
179