AbstractService::getSignatureMethod()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
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();
0 ignored issues
show
Bug introduced by
The method getRequestTokenSecret() does not exist on OAuth\Common\Token\TokenInterface. It seems like you code against a sub-type of OAuth\Common\Token\TokenInterface such as OAuth\OAuth1\Token\TokenInterface or OAuth\OAuth1\Token\StdOAuth1Token. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
            /** @scrutinizer ignore-call */ 
82
            $tokenSecret = $storedRequestToken->getRequestTokenSecret();
Loading history...
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()),
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