Completed
Push — 3.x ( 497b30...ca6623 )
by Дмитрий
03:36 queued 53s
created

AbstractProvider::createAccessToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * SocialConnect project
4
 * @author: Patsura Dmitry https://github.com/ovr <[email protected]>
5
 */
6
declare(strict_types=1);
7
8
namespace SocialConnect\OAuth1;
9
10
use Psr\Http\Message\ResponseInterface;
11
use SocialConnect\Common\Exception\Unsupported;
12
use SocialConnect\Common\HttpStack;
13
use SocialConnect\OAuth1\Exception\UnknownAuthorization;
14
use SocialConnect\OAuth1\Signature\AbstractSignatureMethod;
15
use SocialConnect\Provider\AbstractBaseProvider;
16
use SocialConnect\Provider\AccessTokenInterface;
17
use SocialConnect\Provider\Consumer;
18
use SocialConnect\Provider\Exception\InvalidAccessToken;
19
use SocialConnect\Provider\Exception\InvalidResponse;
20
use SocialConnect\OAuth1\Exception\InvalidRequestToken;
21
use SocialConnect\OAuth1\Signature\MethodHMACSHA1;
22
use SocialConnect\Provider\Session\SessionInterface;
23
24
abstract class AbstractProvider extends AbstractBaseProvider
25
{
26
    /**
27
     * @var string
28
     */
29
    protected $oauth1Version = '1.0a';
30
31
    /**
32
     * @var string
33
     */
34
    protected $requestTokenMethod = 'POST';
35
36
    /**
37
     * @var Consumer
38
     */
39
    protected $consumer;
40
41
    /**
42
     * @var Token
43
     */
44
    protected $consumerToken;
45
46
    /**
47
     * @var array
48
     */
49
    protected $scope = [];
50
51
    /**
52
     * @var AbstractSignatureMethod
53
     */
54
    protected $signature;
55
56
    /**
57
     * {@inheritDoc}
58
     */
59 32
    public function __construct(HttpStack $httpStack, SessionInterface $session, array $parameters)
60
    {
61 32
        parent::__construct($httpStack, $session, $parameters);
62
63 32
        $this->consumerToken = new Token('', '');
64 32
        $this->signature = new MethodHMACSHA1();
65 32
    }
66
67
    /**
68
     * @return string
69
     */
70
    abstract public function getAuthorizeUri();
71
72
    /**
73
     * @return string
74
     */
75
    abstract public function getRequestTokenUri();
76
77
    /**
78
     * @return string
79
     */
80
    abstract public function getRequestTokenAccessUri();
81
82
    /**
83
     * @return Token
84
     * @throws InvalidRequestToken
85
     * @throws InvalidResponse
86
     * @throws \Psr\Http\Client\ClientExceptionInterface
87
     */
88
    protected function requestAuthToken()
89
    {
90
        $parameters = [];
91
92
        /**
93
         * OAuth Core 1.0 Revision A: oauth_callback: An absolute URL to which the Service Provider will redirect
94
         * the User back when the Obtaining User Authorization step is completed.
95
         *
96
         * http://oauth.net/core/1.0a/#auth_step1
97
         */
98
        if ('1.0a' === $this->oauth1Version) {
99
            $parameters['oauth_callback'] = $this->getRedirectUrl();
100
        }
101
102
        $response = $this->oauthRequest(
103
            $this->getRequestTokenUri(),
104
            $this->requestTokenMethod,
105
            $parameters
106
        );
107
108
        $token = $this->parseToken($response->getBody()->getContents());
109
        $this->session->set('oauth1_request_token', $token);
110
111
        return $token;
112
    }
113
114
    /**
115
     * Parse Token from response's $body
116
     *
117
     * @param string|boolean $body
118
     * @return Token
119
     * @throws InvalidRequestToken
120
     * @throws InvalidResponse
121
     */
122
    public function parseToken($body)
123
    {
124
        if (empty($body)) {
125
            throw new InvalidResponse('Provider response with empty body');
126
        }
127
128
        parse_str($body, $token);
129
        if (!is_array($token) || !isset($token['oauth_token']) || !isset($token['oauth_token_secret'])) {
130
            throw new InvalidRequestToken;
131
        }
132
133
        return new Token($token['oauth_token'], $token['oauth_token_secret']);
134
    }
135
136
    /**
137
     * @param string $method
138
     * @param string $uri
139
     * @param array $query
140
     * @return string
141
     */
142 5
    public function getSignatureBaseString(string $method, string $uri, array $query): string
143
    {
144
        $parts = [
145 5
            $method,
146 5
            $uri,
147 5
            Util::buildHttpQuery(
148 5
                $query
149
            )
150
        ];
151
152 5
        $parts = Util::urlencodeRFC3986($parts);
153
154 5
        return implode('&', $parts);
155
    }
156
157 5
    public function authorizationHeader(array $query)
158
    {
159 5
        ksort($query);
160
161 5
        $parameters = http_build_query(
162 5
            $query,
163 5
            '',
164 5
            ', ',
165 5
            PHP_QUERY_RFC3986
166
        );
167
168 5
        return "OAuth $parameters";
169
    }
170
171 4
    public function prepareRequest(string $method, string $uri, array &$headers, array &$query, AccessTokenInterface $accessToken = null): void
172
    {
173 4
        $headers['Accept'] = 'application/json';
174
175 4
        $query['oauth_version'] = '1.0';
176 4
        $query['oauth_nonce'] = $this->generateState();
177 4
        $query['oauth_timestamp'] = time();
178 4
        $query['oauth_consumer_key'] = $this->consumer->getKey();
179
180 4
        if ($accessToken) {
181 3
            $query['oauth_token'] = $accessToken->getToken();
182
        }
183
184 4
        $query['oauth_signature_method'] = $this->signature->getName();
185 4
        $query['oauth_signature'] = $this->signature->buildSignature(
186 4
            $this->getSignatureBaseString($method, $uri, $query),
187 4
            $this->consumer,
188 4
            $this->consumerToken
189
        );
190
191 4
        $headers['Authorization'] = $this->authorizationHeader($query);
192 4
    }
193
194
    /**
195
     * @param string $uri
196
     * @param string $method
197
     * @param array $query
198
     * @param array|null $payload
199
     * @param array $headers
200
     * @return ResponseInterface
201
     * @throws InvalidResponse
202
     * @throws \Psr\Http\Client\ClientExceptionInterface
203
     * @throws \Exception
204
     */
205 1
    protected function oauthRequest($uri, $method = 'GET', array $query = [], array $payload = null, $headers = []): ResponseInterface
206
    {
207 1
        $headers = array_merge([
208 1
            'Accept' => 'application/json'
209
        ], $headers);
210
211 1
        if ($method === 'POST') {
212
            $headers['Content-Type'] = 'application/x-www-form-urlencoded';
213
        }
214
215 1
        $query['oauth_version'] = '1.0';
216 1
        $query['oauth_nonce'] = $this->generateState();
217 1
        $query['oauth_timestamp'] = time();
218 1
        $query['oauth_consumer_key'] = $this->consumer->getKey();
219
220 1
        $query['oauth_signature_method'] = $this->signature->getName();
221 1
        $query['oauth_signature'] = $this->signature->buildSignature(
222 1
            $this->getSignatureBaseString($method, $uri, $query),
223 1
            $this->consumer,
224 1
            $this->consumerToken
225
        );
226
227 1
        $headers['Authorization'] = $this->authorizationHeader($query);
228
229 1
        return $this->executeRequest(
230 1
            $this->createRequest(
231 1
                $method,
232
                $uri,
233
                $query,
234
                $headers,
235
                $payload
236
            )
237
        );
238
    }
239
240
    /**
241
     * {@inheritdoc}
242
     */
243
    public function makeAuthUrl(): string
244
    {
245
        $urlParameters = $this->getAuthUrlParameters();
246
        $urlParameters['oauth_token'] = $this->requestAuthToken()->getKey();
247
248
        return $this->getAuthorizeUri() . '?' . http_build_query($urlParameters, '', '&');
249
    }
250
251
    /**
252
     * @param array $parameters
253
     * @return AccessToken
254
     * @throws InvalidAccessToken
255
     * @throws InvalidResponse
256
     * @throws UnknownAuthorization
257
     */
258
    public function getAccessTokenByRequestParameters(array $parameters)
259
    {
260
        $token = $this->session->get('oauth1_request_token');
261
        if (!$token) {
262
            throw new UnknownAuthorization();
263
        }
264
265
        $this->session->delete('oauth1_request_token');
266
267
        return $this->getAccessToken($token, $parameters['oauth_verifier']);
268
    }
269
270
    /**
271
     * @param Token $token
272
     * @param string $oauthVerifier
273
     * @return AccessToken
274
     * @throws \Psr\Http\Client\ClientExceptionInterface
275
     */
276
    public function getAccessToken(Token $token, string $oauthVerifier)
277
    {
278
        $this->consumerToken = $token;
279
280
        $parameters = [
281
            'oauth_consumer_key' => $this->consumer->getKey(),
282
            'oauth_token' => $token->getKey(),
283
            'oauth_verifier' => $oauthVerifier
284
        ];
285
286
        $response = $this->oauthRequest(
287
            $this->getRequestTokenAccessUri(),
288
            $this->requestTokenMethod,
289
            $parameters
290
        );
291
292
        return $this->parseAccessToken($response->getBody()->getContents());
293
    }
294
295
    /**
296
     * Parse AccessToken from response's $body
297
     *
298
     * @param string $body
299
     * @return AccessToken
300
     * @throws InvalidAccessToken
301
     * @throws InvalidResponse
302
     */
303
    public function parseAccessToken(string $body)
304
    {
305
        if (empty($body)) {
306
            throw new InvalidResponse('Provider response with empty body');
307
        }
308
309
        parse_str($body, $token);
310
311
        if ($token) {
312
            if (!is_array($token)) {
313
                throw new InvalidAccessToken('Response must be array');
314
            }
315
316
            return new AccessToken($token);
317
        }
318
319
        throw new InvalidAccessToken('Server response with not valid/empty JSON');
320
    }
321
322
    /**
323
     * {@inheritDoc}
324
     */
325
    public function createAccessToken(array $information)
326
    {
327
        throw new Unsupported('It\'s usefull to use this method for OAuth1, are you sure?');
328
    }
329
330
    /**
331
     * @return array
332
     */
333
    public function getScope()
334
    {
335
        return $this->scope;
336
    }
337
338
    /**
339
     * @param array $scope
340
     */
341 32
    public function setScope(array $scope)
342
    {
343 32
        $this->scope = $scope;
344 32
    }
345
346
    /**
347
     * @param Token $token
348
     */
349
    public function setConsumerToken(Token $token)
350
    {
351
        $this->consumerToken = $token;
352
    }
353
354
    /**
355
     * @return \SocialConnect\OAuth1\Token
356
     */
357
    public function getConsumerToken()
358
    {
359
        return $this->consumerToken;
360
    }
361
}
362