buildAuthorizationHeaderForAPIRequest()   B
last analyzed

Complexity

Conditions 6
Paths 16

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.7857
c 0
b 0
f 0
cc 6
nc 16
nop 4
1
<?php
2
3
namespace OAuth\OAuth1\Service;
4
5
use DateTime;
6
use OAuth\Common\Consumer\CredentialsInterface;
7
use OAuth\Common\Http\Client\ClientInterface;
8
use OAuth\Common\Http\Uri\UriInterface;
9
use OAuth\Common\Service\AbstractService as BaseAbstractService;
10
use OAuth\Common\Storage\TokenStorageInterface;
11
use OAuth\OAuth1\Signature\SignatureInterface;
12
use OAuth\OAuth1\Token\StdOAuth1Token;
13
use OAuth\OAuth1\Token\TokenInterface;
14
15
abstract class AbstractService extends BaseAbstractService implements ServiceInterface
16
{
17
    /** @const OAUTH_VERSION */
18
    const OAUTH_VERSION = 1;
19
20
    /** @var SignatureInterface */
21
    protected $signature;
22
23
    /** @var null|UriInterface */
24
    protected $baseApiUri;
25
26
    /**
27
     * {@inheritdoc}
28
     */
29
    public function __construct(
30
        CredentialsInterface $credentials,
31
        ClientInterface $httpClient,
32
        TokenStorageInterface $storage,
33
        SignatureInterface $signature,
34
        ?UriInterface $baseApiUri = null
35
    ) {
36
        parent::__construct($credentials, $httpClient, $storage);
37
38
        $this->signature = $signature;
39
        $this->baseApiUri = $baseApiUri;
40
41
        $this->signature->setHashingAlgorithm($this->getSignatureMethod());
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function requestRequestToken()
48
    {
49
        $authorizationHeader = ['Authorization' => $this->buildAuthorizationHeaderForTokenRequest()];
50
        $headers = array_merge($authorizationHeader, $this->getExtraOAuthHeaders());
51
52
        $responseBody = $this->httpClient->retrieveResponse($this->getRequestTokenEndpoint(), [], $headers);
53
54
        $token = $this->parseRequestTokenResponse($responseBody);
55
        $this->storage->storeAccessToken($this->service(), $token);
56
57
        return $token;
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function getAuthorizationUri(array $additionalParameters = [])
64
    {
65
        // Build the url
66
        $url = clone $this->getAuthorizationEndpoint();
67
        foreach ($additionalParameters as $key => $val) {
68
            $url->addToQuery($key, $val);
69
        }
70
71
        return $url;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function requestAccessToken($token, $verifier, $tokenSecret = null)
78
    {
79
        if (null === $tokenSecret) {
80
            $storedRequestToken = $this->storage->retrieveAccessToken($this->service());
81
            $tokenSecret = $storedRequestToken->getRequestTokenSecret();
82
        }
83
        $this->signature->setTokenSecret($tokenSecret);
84
85
        $bodyParams = [
86
            'oauth_verifier' => $verifier,
87
        ];
88
89
        $authorizationHeader = [
90
            'Authorization' => $this->buildAuthorizationHeaderForAPIRequest(
91
                'POST',
92
                $this->getAccessTokenEndpoint(),
93
                $this->storage->retrieveAccessToken($this->service()),
0 ignored issues
show
Compatibility introduced by
$this->storage->retrieve...Token($this->service()) of type object<OAuth\Common\Token\TokenInterface> is not a sub-type of object<OAuth\OAuth1\Token\TokenInterface>. It seems like you assume a child interface of the interface OAuth\Common\Token\TokenInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
94
                $bodyParams
95
            ),
96
        ];
97
98
        $headers = array_merge($authorizationHeader, $this->getExtraOAuthHeaders());
99
100
        $responseBody = $this->httpClient->retrieveResponse($this->getAccessTokenEndpoint(), $bodyParams, $headers);
101
102
        $token = $this->parseAccessTokenResponse($responseBody);
103
        $this->storage->storeAccessToken($this->service(), $token);
104
105
        return $token;
106
    }
107
108
    /**
109
     * Refreshes an OAuth1 access token.
110
     *
111
     * @return TokenInterface $token
112
     */
113
    public function refreshAccessToken(TokenInterface $token)
114
    {
115
    }
116
117
    /**
118
     * Sends an authenticated API request to the path provided.
119
     * If the path provided is not an absolute URI, the base API Uri (must be passed into constructor) will be used.
120
     *
121
     * @param string|UriInterface $path
122
     * @param string              $method       HTTP method
123
     * @param array               $body         Request body if applicable (key/value pairs)
124
     * @param array               $extraHeaders Extra headers if applicable.
125
     *                                          These will override service-specific any defaults.
126
     *
127
     * @return string
128
     */
129
    public function request($path, $method = 'GET', $body = null, array $extraHeaders = [])
130
    {
131
        $uri = $this->determineRequestUriFromPath($path, $this->baseApiUri);
132
133
        /** @var StdOAuth1Token $token */
134
        $token = $this->storage->retrieveAccessToken($this->service());
135
        $extraHeaders = array_merge($this->getExtraApiHeaders(), $extraHeaders);
136
        $authorizationHeader = [
137
            'Authorization' => $this->buildAuthorizationHeaderForAPIRequest($method, $uri, $token, $body),
138
        ];
139
        $headers = array_merge($authorizationHeader, $extraHeaders);
140
141
        return $this->httpClient->retrieveResponse($uri, $body, $headers, $method);
142
    }
143
144
    /**
145
     * Return any additional headers always needed for this service implementation's OAuth calls.
146
     *
147
     * @return array
148
     */
149
    protected function getExtraOAuthHeaders()
150
    {
151
        return [];
152
    }
153
154
    /**
155
     * Return any additional headers always needed for this service implementation's API calls.
156
     *
157
     * @return array
158
     */
159
    protected function getExtraApiHeaders()
160
    {
161
        return [];
162
    }
163
164
    /**
165
     * Builds the authorization header for getting an access or request token.
166
     *
167
     * @return string
168
     */
169
    protected function buildAuthorizationHeaderForTokenRequest(array $extraParameters = [])
170
    {
171
        $parameters = $this->getBasicAuthorizationHeaderInfo();
172
        $parameters = array_merge($parameters, $extraParameters);
173
        $parameters['oauth_signature'] = $this->signature->getSignature(
174
            $this->getRequestTokenEndpoint(),
175
            $parameters,
176
            'POST'
177
        );
178
179
        $authorizationHeader = 'OAuth ';
180
        $delimiter = '';
181
        foreach ($parameters as $key => $value) {
182
            $authorizationHeader .= $delimiter . rawurlencode($key) . '="' . rawurlencode($value) . '"';
183
184
            $delimiter = ', ';
185
        }
186
187
        return $authorizationHeader;
188
    }
189
190
    /**
191
     * Builds the authorization header for an authenticated API request.
192
     *
193
     * @param string         $method
194
     * @param UriInterface   $uri        The uri the request is headed
195
     * @param array          $bodyParams Request body if applicable (key/value pairs)
196
     *
197
     * @return string
198
     */
199
    protected function buildAuthorizationHeaderForAPIRequest(
200
        $method,
201
        UriInterface $uri,
202
        TokenInterface $token,
203
        $bodyParams = null
204
    ) {
205
        $this->signature->setTokenSecret($token->getAccessTokenSecret());
206
        $authParameters = $this->getBasicAuthorizationHeaderInfo();
207
        if (isset($authParameters['oauth_callback'])) {
208
            unset($authParameters['oauth_callback']);
209
        }
210
211
        $authParameters = array_merge($authParameters, ['oauth_token' => $token->getAccessToken()]);
212
213
        $signatureParams = (is_array($bodyParams)) ? array_merge($authParameters, $bodyParams) : $authParameters;
214
        $authParameters['oauth_signature'] = $this->signature->getSignature($uri, $signatureParams, $method);
215
216
        if (is_array($bodyParams) && isset($bodyParams['oauth_session_handle'])) {
217
            $authParameters['oauth_session_handle'] = $bodyParams['oauth_session_handle'];
218
            unset($bodyParams['oauth_session_handle']);
219
        }
220
221
        $authorizationHeader = 'OAuth ';
222
        $delimiter = '';
223
224
        foreach ($authParameters as $key => $value) {
225
            $authorizationHeader .= $delimiter . rawurlencode($key) . '="' . rawurlencode($value) . '"';
226
            $delimiter = ', ';
227
        }
228
229
        return $authorizationHeader;
230
    }
231
232
    /**
233
     * Builds the authorization header array.
234
     *
235
     * @return array
236
     */
237
    protected function getBasicAuthorizationHeaderInfo()
238
    {
239
        $dateTime = new DateTime();
240
        $headerParameters = [
241
            'oauth_callback' => $this->credentials->getCallbackUrl(),
242
            'oauth_consumer_key' => $this->credentials->getConsumerId(),
243
            'oauth_nonce' => $this->generateNonce(),
244
            'oauth_signature_method' => $this->getSignatureMethod(),
245
            'oauth_timestamp' => $dateTime->format('U'),
246
            'oauth_version' => $this->getVersion(),
247
        ];
248
249
        return $headerParameters;
250
    }
251
252
    /**
253
     * Pseudo random string generator used to build a unique string to sign each request.
254
     *
255
     * @param int $length
256
     *
257
     * @return string
258
     */
259
    protected function generateNonce($length = 32)
260
    {
261
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
262
263
        $nonce = '';
264
        $maxRand = strlen($characters) - 1;
265
        for ($i = 0; $i < $length; ++$i) {
266
            $nonce .= $characters[mt_rand(0, $maxRand)];
267
        }
268
269
        return $nonce;
270
    }
271
272
    /**
273
     * @return string
274
     */
275
    protected function getSignatureMethod()
276
    {
277
        return 'HMAC-SHA1';
278
    }
279
280
    /**
281
     * This returns the version used in the authorization header of the requests.
282
     *
283
     * @return string
284
     */
285
    protected function getVersion()
286
    {
287
        return '1.0';
288
    }
289
290
    /**
291
     * Parses the request token response and returns a TokenInterface.
292
     * This is only needed to verify the `oauth_callback_confirmed` parameter. The actual
293
     * parsing logic is contained in the access token parser.
294
     *
295
     * @abstract
296
     *
297
     * @param string $responseBody
298
     *
299
     * @return TokenInterface
300
     */
301
    abstract protected function parseRequestTokenResponse($responseBody);
302
303
    /**
304
     * Parses the access token response and returns a TokenInterface.
305
     *
306
     * @abstract
307
     *
308
     * @param string $responseBody
309
     *
310
     * @return TokenInterface
311
     */
312
    abstract protected function parseAccessTokenResponse($responseBody);
313
}
314