OAuthClient   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 71
c 2
b 0
f 0
dl 0
loc 136
rs 10
wmc 19

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A fetchInitialTokens() 0 11 1
A refreshAccessToken() 0 38 5
A storeTokens() 0 4 1
A request() 0 17 6
A parseAndStoreTokens() 0 11 2
A getAccessToken() 0 13 3
1
<?php
2
3
namespace Sarahman\OauthTokensClient;
4
5
use Exception;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\Exception\RequestException;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\SimpleCache\CacheInterface;
10
11
class OAuthClient
12
{
13
    private static $accessTokenKey;
14
    private static $refreshTokenKey;
15
    private static $lockKey;
16
17
    /** @var Client */
18
    private $httpClient;
19
20
    /** @var CacheInterface */
21
    private $cache;
22
23
    private $tokenUrl;
24
    private $refreshUrl;
25
    private $clientId;
26
    private $clientSecret;
27
28
    public function __construct(Client $httpClient, CacheInterface $cache, array $oauthConfig, array $tokenPrefixes, $lockKey)
29
    {
30
        $this->httpClient = $httpClient;
31
        $this->cache = $cache;
32
        $this->tokenUrl = $oauthConfig['TOKEN_URL'];
33
        $this->refreshUrl = $oauthConfig['REFRESH_URL'];
34
        $this->clientId = $oauthConfig['CLIENT_ID'];
35
        $this->clientSecret = $oauthConfig['CLIENT_SECRET'];
36
37
        self::$accessTokenKey = $tokenPrefixes['ACCESS'];
38
        self::$refreshTokenKey = $tokenPrefixes['REFRESH'];
39
        self::$lockKey = $lockKey;
40
    }
41
42
    public function request($method, $uri, array $options = array(), $retryCount = 1)
43
    {
44
        isset($options['headers']) || $options['headers'] = array();
45
        $options['headers']['Authorization'] = "Bearer {$this->getAccessToken()}";
46
47
        try {
48
            return $this->httpClient->request($method, $uri, $options);
49
        } catch (RequestException $e) {
50
            $response = $e->getResponse();
51
52
            if ($response && $response->getStatusCode() === 401 && $retryCount > 0) {
53
                $options['headers']['Authorization'] = "Bearer {$this->refreshAccessToken()}";
54
55
                return $this->request($method, $uri, $options, $retryCount - 1);
56
            }
57
58
            throw $e;
59
        }
60
    }
61
62
    private function getAccessToken()
63
    {
64
        $token = $this->cache->get(self::$accessTokenKey);
65
66
        if ($token) {
67
            return $token;
68
        }
69
70
        while ($this->cache->has(self::$lockKey)) {
71
            usleep(50000); // wait 50ms
72
        }
73
74
        return $this->refreshAccessToken();
75
    }
76
77
    private function refreshAccessToken()
78
    {
79
        if ($this->cache->has(self::$lockKey)) {
80
            while ($this->cache->has(self::$lockKey)) {
81
                usleep(50000);
82
            }
83
84
            return $this->cache->get(self::$accessTokenKey);
85
        }
86
87
        $this->cache->set(self::$lockKey, true, 10);
88
89
        try {
90
            $token = '';
91
            $refreshToken = $this->cache->get(self::$refreshTokenKey);
92
93
            if (!$refreshToken) {
94
                $token = $this->fetchInitialTokens();
95
            } else {
96
                $response = $this->httpClient->post($this->refreshUrl, array(
97
                    'form_params' => array(
98
                        'grant_type'    => 'refresh_token',
99
                        'refresh_token' => $refreshToken,
100
                        'client_id'     => $this->clientId,
101
                        'client_secret' => $this->clientSecret,
102
                    ),
103
                ));
104
105
                $token = $this->parseAndStoreTokens($response);
106
            }
107
        } catch (Exception $e) {
108
            $this->cache->delete(self::$lockKey);
109
            throw $e;
110
        }
111
112
        $this->cache->delete(self::$lockKey);
113
114
        return $token;
115
    }
116
117
    private function fetchInitialTokens()
118
    {
119
        $response = $this->httpClient->post($this->tokenUrl, array(
120
            'form_params' => array(
121
                'grant_type'    => 'client_credentials',
122
                'client_id'     => $this->clientId,
123
                'client_secret' => $this->clientSecret,
124
            ),
125
        ));
126
127
        return $this->parseAndStoreTokens($response);
128
    }
129
130
    private function parseAndStoreTokens(ResponseInterface $response)
131
    {
132
        $data = json_decode((string) $response->getBody(), true);
133
134
        if (json_last_error()) {
135
            return '';
136
        }
137
138
        $this->storeTokens($data);
139
140
        return $data['access_token'];
141
    }
142
143
    private function storeTokens(array $data)
144
    {
145
        $this->cache->set(self::$accessTokenKey, $data['access_token'], $data['expires_in'] - 30);
146
        $this->cache->set(self::$refreshTokenKey, $data['refresh_token']);
147
    }
148
}
149