GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 64206a...366962 )
by François
19:13 queued 13:33
created

OAuth2Client::getAccessToken()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 45
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 45
rs 8.439
cc 5
eloc 25
nc 5
nop 3
1
<?php
2
/**
3
 * Copyright 2016 François Kooman <[email protected]>.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
namespace fkooman\OAuth\Client;
18
19
use fkooman\OAuth\Client\Exception\OAuthException;
20
use InvalidArgumentException;
21
use DomainException;
22
23
/**
24
 * OAuth 2.0 Client. Helper class to make it easy to obtain an access token
25
 * from an OAuth 2.0 provider.
26
 */
27
class OAuth2Client
28
{
29
    /** @var Provider */
30
    private $provider;
31
32
    /** @var HttpClientInterface */
33
    private $httpClient;
34
35
    /** @var RandomInterface */
36
    private $random;
37
38
    /**
39
     * Instantiate an OAuth 2.0 Client.
40
     *
41
     * @param Provider            $provider   the OAuth 2.0 provider configuration
42
     * @param HttpClientInterface $httpClient the HTTP client implementation
43
     * @param RandomInterface     $random     the random implementation
44
     */
45
    public function __construct(Provider $provider, HttpClientInterface $httpClient, RandomInterface $random = null)
46
    {
47
        $this->provider = $provider;
48
        $this->httpClient = $httpClient;
49
        if (is_null($random)) {
50
            $random = new Random();
51
        }
52
        $this->random = $random;
53
    }
54
55
    /**
56
     * Obtain an authorization request URL to start the authorization process
57
     * at the OAuth provider.
58
     *
59
     * @param string $scope       the space separated scope tokens
60
     * @param string $redirectUri the URL to redirect back to after coming back
61
     *                            from the OAuth provider (callback URL)
62
     *
63
     * @return string the authorization request URL
64
     *
65
     * @see https://tools.ietf.org/html/rfc6749#section-3.3
66
     * @see https://tools.ietf.org/html/rfc6749#section-3.1.2
67
     */
68
    public function getAuthorizationRequestUri($scope, $redirectUri)
69
    {
70
        $state = $this->random->get();
71
72
        $queryParams = http_build_query(
73
            [
74
                'client_id' => $this->provider->getId(),
75
                'redirect_uri' => $redirectUri,
76
                'scope' => $scope,
77
                'state' => $state,
78
                'response_type' => 'code',
79
            ],
80
            '&'
81
        );
82
83
        return sprintf(
84
            '%s%s%s',
85
            $this->provider->getAuthorizationEndpoint(),
86
            false === strpos($this->provider->getAuthorizationEndpoint(), '?') ? '?' : '&',
87
            $queryParams
88
        );
89
    }
90
91
    /**
92
     * Obtain the access token from the OAuth provider after returning from the
93
     * OAuth provider on the redirectUri (callback URL).
94
     *
95
     * @param string $authorizationRequestUri    the original authorization
96
     *                                           request URL as obtained by getAuthorzationRequestUri
97
     * @param string $authorizationResponseCode  the code passed to the 'code'
98
     *                                           query parameter on the callback URL
99
     * @param string $authorizationResponseState the state passed to the 'state'
100
     *                                           query parameter on the callback URL
101
     *
102
     * @return AccessToken
103
     */
104
    public function getAccessToken($authorizationRequestUri, $authorizationResponseCode, $authorizationResponseState)
105
    {
106
        self::requireNonEmptyStrings(func_get_args());
107
108
        // parse our authorizationRequestUri to extract the state
109
        if (false === strpos($authorizationRequestUri, '?')) {
110
            throw new OAuthException('invalid authorizationRequestUri');
111
        }
112
113
        parse_str(explode('?', $authorizationRequestUri)[1], $queryParams);
114
115
        if (!isset($queryParams['state'])) {
116
            throw new OAuthException('state missing from authorizationRequestUri');
117
        }
118
119
        if (!isset($queryParams['redirect_uri'])) {
120
            throw new OAuthException('redirect_uri missing from authorizationRequestUri');
121
        }
122
123
        if ($authorizationResponseState !== $queryParams['state']) {
124
            throw new OAuthException('state from authorizationRequestUri does not equal authorizationResponseState');
125
        }
126
127
        // prepare access_token request
128
        $tokenRequestData = [
129
            'client_id' => $this->provider->getId(),
130
            'grant_type' => 'authorization_code',
131
            'code' => $authorizationResponseCode,
132
            'redirect_uri' => $queryParams['redirect_uri'],
133
        ];
134
135
        $responseData = self::validateTokenResponse(
136
            $this->httpClient->post(
137
                $this->provider,
138
                $tokenRequestData
139
            )
140
        );
141
142
        return new AccessToken(
143
            $responseData['access_token'],
144
            $responseData['token_type'],
145
            $responseData['scope'],
146
            $responseData['expire_in']
147
        );
148
    }
149
150
    private static function validateTokenResponse($jsonString)
151
    {
152
        $responseData = json_decode($jsonString, true);
153
        if (JSON_ERROR_NONE !== json_last_error()) {
154
            throw new OAuthException('non-JSON data received from token endpoint');
155
        }
156
157
        if (!is_array($responseData)) {
158
            throw new OAuthException('invalid data received from token endpoint');
159
        }
160
161
        if (!isset($responseData['access_token'])) {
162
            throw new OAuthException('no access_token received from token endpoint');
163
        }
164
165
        if (!isset($responseData['token_type'])) {
166
            throw new OAuthException('no token_type received from token endpoint');
167
        }
168
169
        if (!isset($responseData['scope'])) {
170
            $responseData['scope'] = null;
171
        }
172
173
        if (!isset($responseData['expires_in'])) {
174
            $responseData['expire_in'] = null;
175
        }
176
177
        return $responseData;
178
    }
179
180
    private static function requireNonEmptyStrings(array $strs)
181
    {
182
        foreach ($strs as $no => $str) {
183
            if (!is_string($str)) {
184
                throw new InvalidArgumentException(sprintf('parameter %d must be string', $no));
185
            }
186
            if (0 >= strlen($str)) {
187
                throw new DomainException(sprintf('parameter %d must be non-empty', $no));
188
            }
189
        }
190
    }
191
}
192