Skip to content
This repository was archived by the owner on Mar 23, 2024. It is now read-only.

Commit 519c277

Browse files
committed
🛀
1 parent 3e58ef3 commit 519c277

File tree

9 files changed

+61
-64
lines changed

9 files changed

+61
-64
lines changed

src/Core/ClientCredentials.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ interface ClientCredentials{
2020

2121
/**
2222
* Obtains an OAuth2 client credentials token and returns an AccessToken
23-
*
24-
* @param string[]|null $scopes
25-
*
26-
* @throws \chillerlan\OAuth\Providers\ProviderException
2723
*/
2824
public function getClientCredentialsToken(array|null $scopes = null):AccessToken;
2925

src/Core/OAuth1Interface.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,13 @@ interface OAuth1Interface extends OAuthInterface{
2222
* @see https://datatracker.ietf.org/doc/html/rfc5849#section-2.1
2323
*
2424
* @see \chillerlan\OAuth\Core\OAuth1Provider::getAuthURL()
25-
*
26-
* @throws \chillerlan\OAuth\Providers\ProviderException
2725
*/
2826
public function getRequestToken():AccessToken;
2927

3028
/**
3129
* Obtains an OAuth1 access token with the given $token and $verifier and returns an AccessToken object.
3230
*
3331
* @see https://datatracker.ietf.org/doc/html/rfc5849#section-2.3
34-
*
35-
* @throws \chillerlan\OAuth\Providers\ProviderException
3632
*/
3733
public function getAccessToken(string $token, string $verifier):AccessToken;
3834

src/Core/OAuth1Provider.php

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use chillerlan\HTTP\Utils\{MessageUtil, QueryUtil};
1515
use chillerlan\OAuth\Providers\ProviderException;
1616
use Psr\Http\Message\{RequestInterface, ResponseInterface, UriInterface};
17-
use function array_merge, base64_encode, hash_hmac, implode, in_array, random_bytes, sodium_bin2hex, strtoupper, time;
17+
use function array_merge, base64_encode, hash_hmac, implode, random_bytes, sodium_bin2hex, sprintf, strtoupper, time;
1818

1919
/**
2020
* Implements an abstract OAuth1 provider with all methods required by the OAuth1Interface.
@@ -61,7 +61,7 @@ public function getRequestToken():AccessToken{
6161
;
6262

6363
foreach($this::HEADERS_AUTH as $header => $value){
64-
$request = $request->withAddedHeader($header, $value);
64+
$request = $request->withHeader($header, $value);
6565
}
6666

6767
return $this->parseTokenResponse($this->http->sendRequest($request), true);
@@ -75,35 +75,31 @@ public function getRequestToken():AccessToken{
7575
*
7676
* @throws \chillerlan\OAuth\Providers\ProviderException
7777
*/
78-
protected function parseTokenResponse(ResponseInterface $response, bool $checkCallbackConfirmed):AccessToken{
78+
protected function parseTokenResponse(ResponseInterface $response, bool $checkCallback):AccessToken{
7979
$data = QueryUtil::parse(MessageUtil::decompress($response));
8080

8181
if(empty($data)){
8282
throw new ProviderException('unable to parse token response');
8383
}
8484
elseif(isset($data['error'])){
85-
throw new ProviderException('error retrieving access token: '.$data['error']);
85+
throw new ProviderException(sprintf('error retrieving access token: "%s"', $data['error']));
8686
}
8787
elseif(!isset($data['oauth_token']) || !isset($data['oauth_token_secret'])){
8888
throw new ProviderException('invalid token');
8989
}
9090

91-
if(
92-
$checkCallbackConfirmed
93-
&& (!isset($data['oauth_callback_confirmed']) || $data['oauth_callback_confirmed'] !== 'true')
94-
){
91+
if($checkCallback && (!isset($data['oauth_callback_confirmed']) || $data['oauth_callback_confirmed'] !== 'true')){
9592
throw new ProviderException('oauth callback unconfirmed');
9693
}
9794

98-
$token = $this->createAccessToken();
99-
95+
$token = $this->createAccessToken();
10096
$token->accessToken = $data['oauth_token'];
10197
$token->accessTokenSecret = $data['oauth_token_secret'];
10298
$token->expires = AccessToken::EOL_NEVER_EXPIRES;
10399

104100
unset($data['oauth_token'], $data['oauth_token_secret']);
105101

106-
$token->extraParams = $data;
102+
$token->extraParams = $data;
107103

108104
$this->storage->storeAccessToken($token, $this->serviceName);
109105

@@ -126,20 +122,28 @@ protected function nonce():string{
126122
*
127123
* @throws \chillerlan\OAuth\Providers\ProviderException
128124
*/
129-
protected function getSignature(string $url, array $params, string $method, string|null $accessTokenSecret = null):string{
130-
$parsed = $this->uriFactory->createUri($url);
125+
protected function getSignature(
126+
UriInterface|string $url,
127+
array $params,
128+
string $method,
129+
string|null $accessTokenSecret = null
130+
):string{
131+
132+
if(!$url instanceof UriInterface){
133+
$url = $this->uriFactory->createUri($url);
134+
}
131135

132-
if($parsed->getHost() == '' || $parsed->getScheme() === '' || !in_array($parsed->getScheme(), ['http', 'https'])){
133-
throw new ProviderException('getSignature: invalid url');
136+
if($url->getHost() === '' || $url->getScheme() !== 'https'){
137+
throw new ProviderException(sprintf('getSignature: invalid url: "%s"', $url));
134138
}
135139

136-
$signatureParams = array_merge(QueryUtil::parse($parsed->getQuery()), $params);
137-
$url = (string)$parsed->withQuery('')->withFragment('');
140+
$signatureParams = array_merge(QueryUtil::parse($url->getQuery()), $params);
141+
$url = $url->withQuery('')->withFragment('');
138142

139143
unset($signatureParams['oauth_signature']);
140144

141145
// https://datatracker.ietf.org/doc/html/rfc5849#section-3.4.1.1
142-
$data = QueryUtil::recursiveRawurlencode([strtoupper($method), $url, QueryUtil::build($signatureParams)]);
146+
$data = QueryUtil::recursiveRawurlencode([strtoupper($method), (string)$url, QueryUtil::build($signatureParams)]);
143147

144148
// https://datatracker.ietf.org/doc/html/rfc5849#section-3.4.2
145149
$key = QueryUtil::recursiveRawurlencode([$this->options->secret, ($accessTokenSecret ?? '')]);
@@ -170,7 +174,7 @@ public function getRequestAuthorization(RequestInterface $request, AccessToken $
170174
$uri = $request->getUri();
171175
$query = QueryUtil::parse($uri->getQuery());
172176

173-
$parameters = [
177+
$params = [
174178
'oauth_consumer_key' => $this->options->key,
175179
'oauth_nonce' => $this->nonce(),
176180
'oauth_signature_method' => 'HMAC-SHA1',
@@ -179,18 +183,13 @@ public function getRequestAuthorization(RequestInterface $request, AccessToken $
179183
'oauth_version' => '1.0',
180184
];
181185

182-
$parameters['oauth_signature'] = $this->getSignature(
183-
(string)$uri->withQuery('')->withFragment(''),
184-
array_merge($query, $parameters),
185-
$request->getMethod(),
186-
$token->accessTokenSecret
187-
);
186+
$params['oauth_signature'] = $this->getSignature($uri, $params, $request->getMethod(), $token->accessTokenSecret);
188187

189188
if(isset($query['oauth_session_handle'])){
190-
$parameters['oauth_session_handle'] = $query['oauth_session_handle']; // @codeCoverageIgnore
189+
$params['oauth_session_handle'] = $query['oauth_session_handle']; // @codeCoverageIgnore
191190
}
192191

193-
return $request->withHeader('Authorization', 'OAuth '.QueryUtil::build($parameters, null, ', ', '"'));
192+
return $request->withHeader('Authorization', 'OAuth '.QueryUtil::build($params, null, ', ', '"'));
194193
}
195194

196195
}

src/Core/OAuth2Interface.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ interface OAuth2Interface extends OAuthInterface{
6767
*
6868
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1
6969
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
70-
*
71-
* @throws \chillerlan\OAuth\Providers\ProviderException
7270
*/
7371
public function getAccessToken(string $code, string|null $state = null):AccessToken;
7472

@@ -77,9 +75,6 @@ public function getAccessToken(string $code, string|null $state = null):AccessTo
7775
* and returns a PSR-7 UriInterface with all necessary parameters set
7876
*
7977
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1
80-
*
81-
* @param array|null $params
82-
* @param string[]|null $scopes
8378
*/
8479
public function getAuthURL(array|null $params = null, array|null $scopes = null):UriInterface;
8580

src/Core/OAuth2Provider.php

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ abstract class OAuth2Provider extends OAuthProvider implements OAuth2Interface{
4141

4242
/**
4343
* @inheritDoc
44+
* @param string[] $scopes
4445
*/
4546
public function getAuthURL(array|null $params = null, array|null $scopes = null):UriInterface{
4647
$params ??= [];
@@ -84,29 +85,29 @@ protected function parseTokenResponse(ResponseInterface $response):AccessToken{
8485

8586
foreach(['error_description', 'error'] as $field){
8687
if(isset($data[$field])){
87-
throw new ProviderException('error retrieving access token: "'.$data[$field].'"');
88+
throw new ProviderException(sprintf('error retrieving access token: "%s"', $data[$field]));
8889
}
8990
}
9091

9192
if(!isset($data['access_token'])){
9293
throw new ProviderException('token missing');
9394
}
9495

95-
$token = $this->createAccessToken();
96+
$scopes = ($data['scope'] ?? $data['scopes'] ?? []);
9697

98+
if(!is_array($scopes)){
99+
$scopes = explode($this::SCOPE_DELIMITER, $scopes);
100+
}
101+
102+
$token = $this->createAccessToken();
97103
$token->accessToken = $data['access_token'];
98104
$token->expires = ($data['expires_in'] ?? AccessToken::EOL_NEVER_EXPIRES);
99105
$token->refreshToken = ($data['refresh_token'] ?? null);
106+
$token->scopes = $scopes;
100107

101-
if(isset($data['scope']) || isset($data['scopes'])){
102-
$scope = ($data['scope'] ?? $data['scopes'] ?? []);
103-
104-
$token->scopes = (is_array($scope)) ? $scope : explode($this::SCOPE_DELIMITER, $scope);
105-
}
108+
unset($data['access_token'], $data['refresh_token'], $data['expires_in'], $data['scope'], $data['scopes']);
106109

107-
unset($data['expires_in'], $data['refresh_token'], $data['access_token'], $data['scope'], $data['scopes']);
108-
109-
$token->extraParams = $data;
110+
$token->extraParams = $data;
110111

111112
return $token;
112113
}
@@ -164,6 +165,7 @@ public function getRequestAuthorization(RequestInterface $request, AccessToken $
164165
}
165166

166167
/**
168+
* @param string[] $scopes
167169
* @implements \chillerlan\OAuth\Core\ClientCredentials
168170
* @throws \chillerlan\OAuth\Providers\ProviderException
169171
*/
@@ -188,7 +190,7 @@ public function getClientCredentialsToken(array|null $scopes = null):AccessToken
188190
;
189191

190192
foreach($this::HEADERS_AUTH as $header => $value){
191-
$request = $request->withAddedHeader($header, $value);
193+
$request = $request->withHeader($header, $value);
192194
}
193195

194196
$token = $this->parseTokenResponse($this->http->sendRequest($request));
@@ -241,7 +243,7 @@ public function refreshAccessToken(AccessToken|null $token = null):AccessToken{
241243
;
242244

243245
foreach($this::HEADERS_AUTH as $header => $value){
244-
$request = $request->withAddedHeader($header, $value);
246+
$request = $request->withHeader($header, $value);
245247
}
246248

247249
$newToken = $this->parseTokenResponse($this->http->sendRequest($request));
@@ -267,13 +269,13 @@ public function checkState(string|null $state = null):void{
267269
}
268270

269271
if(empty($state) || !$this->storage->hasCSRFState($this->serviceName)){
270-
throw new ProviderException('invalid state for '.$this->serviceName);
272+
throw new ProviderException(sprintf('invalid state for "%s"', $this->serviceName));
271273
}
272274

273275
$knownState = $this->storage->getCSRFState($this->serviceName);
274276

275277
if(!hash_equals($knownState, $state)){
276-
throw new ProviderException('invalid CSRF state: '.$this->serviceName.' '.$state);
278+
throw new ProviderException(sprintf('invalid CSRF state for provider "%s": %s', $this->serviceName, $state));
277279
}
278280

279281
}

src/Core/OAuthInterface.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ interface OAuthInterface extends ClientInterface{
3333
/**
3434
* additional headers to use during authentication
3535
*
36+
* Note: must not contain: Accept-Encoding, Authorization, Content-Length, Content-Type
37+
*
3638
* @var array
3739
*/
3840
public const HEADERS_AUTH = [];
3941

4042
/**
4143
* additional headers to use during API access
4244
*
45+
* Note: must not contain: Authorization
46+
*
4347
* @var array
4448
*/
4549
public const HEADERS_API = [];
@@ -56,16 +60,13 @@ public function getAuthURL(array|null $params = null):UriInterface;
5660
* Authorizes the $request with the credentials from the given $token
5761
* and returns a PSR-7 RequestInterface with all necessary headers and/or parameters set
5862
*
59-
* @throws \chillerlan\OAuth\Providers\ProviderException
6063
* @internal
6164
*/
6265
public function getRequestAuthorization(RequestInterface $request, AccessToken $token):RequestInterface;
6366

6467
/**
6568
* Prepares an API request to $path with the given parameters, gets authorization, fires the request
6669
* and returns a PSR-7 ResponseInterface with the corresponding API response
67-
*
68-
* @throws \chillerlan\OAuth\Providers\ProviderException
6970
*/
7071
public function request(
7172
string $path,
@@ -119,8 +120,6 @@ public function setUriFactory(UriFactoryInterface $uriFactory):static;
119120
/**
120121
* Returns information about the currently authenticated user (usually a /me or /user endpoint).
121122
* Throws a ProviderException if no such information is available or if the method cannot be implemented.
122-
*
123-
* @throws \chillerlan\OAuth\Providers\ProviderException
124123
*/
125124
public function me():ResponseInterface;
126125

src/Providers/BattleNet.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public function me():ResponseInterface{
9191
try{
9292
$json = MessageUtil::decodeJSON($response);
9393
}
94-
catch(Throwable $e){
94+
catch(Throwable){
9595
}
9696

9797
if(isset($json->error, $json->error_description)){

src/Providers/LastFM.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ protected function parseTokenResponse(ResponseInterface $response):AccessToken{
108108
trigger_error('');
109109
}
110110
}
111-
catch(Throwable $e){
111+
catch(Throwable){
112112
throw new ProviderException('unable to parse token response');
113113
}
114114

tests/Providers/OAuth1ProviderTestAbstract.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,29 @@ public function testGetAuthURL():void{
5757
}
5858

5959
public function testGetSignature():void{
60+
$expected = 'fvkt6r6LhR0TgMvDOGsSlzB7IR4=';
61+
62+
$signature = $this->invokeReflectionMethod(
63+
'getSignature',
64+
['https://localhost/api/whatever', ['foo' => 'bar', 'oauth_signature' => 'should not see me!'], 'GET'],
65+
);
66+
67+
$this::assertSame($expected, $signature);
68+
69+
// the "oauth_signature" parameter should be unset if present
6070
$signature = $this->invokeReflectionMethod(
6171
'getSignature',
62-
['http://localhost/api/whatever', ['foo' => 'bar', 'oauth_signature' => 'should not see me!'], 'GET'],
72+
['https://localhost/api/whatever', ['foo' => 'bar'], 'GET'],
6373
);
6474

65-
$this::assertSame('ygg22quLhpyegiyr7yl4hLAP9S8=', $signature);
75+
$this::assertSame($expected, $signature);
6676
}
6777

6878
public function testGetSignatureInvalidURLException():void{
6979
$this->expectException(ProviderException::class);
7080
$this->expectExceptionMessage('getSignature: invalid url');
7181

72-
$this->invokeReflectionMethod('getSignature', ['whatever', [], 'GET']);
82+
$this->invokeReflectionMethod('getSignature', ['http://localhost/boo', [], 'GET']);
7383
}
7484

7585
public function testGetAccessToken():void{

0 commit comments

Comments
 (0)