Skip to content

Commit 3394180

Browse files
committed
:octocat: simplify basic auth usage in access token request
1 parent 9834f0c commit 3394180

File tree

7 files changed

+43
-104
lines changed

7 files changed

+43
-104
lines changed

src/Core/OAuth2Interface.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ interface OAuth2Interface extends OAuthInterface{
5959
*/
6060
public const SCOPES_DELIMITER = ' ';
6161

62+
/**
63+
* This indicates that the current provider requires an `Authorization: Basic <base64(key:secret)>` header
64+
* in the access token request, rather than the key and secret in the request body.
65+
*
66+
* It saves provider inplementations from the hassle to override the respective methods:
67+
*
68+
* - `OAuth2Provider::getAccessTokenRequestBodyParams()`
69+
* - `OAuth2Provider::sendAccessTokenRequest()`
70+
*
71+
* I'm not sure where to put this: here or a feature interface (it's not exactly a feature).
72+
* I'll leave it here for now, subject to change.
73+
*/
74+
public const USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST = false;
75+
6276
/**
6377
* Obtains an OAuth2 access token with the given $code, verifies the $state
6478
* if the provider implements the CSRFToken interface, and returns an AccessToken object

src/Core/OAuth2Provider.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,16 @@ public function getAccessToken(string $code, string|null $state = null):AccessTo
185185
protected function getAccessTokenRequestBodyParams(string $code):array{
186186

187187
$params = [
188-
'client_id' => $this->options->key,
189-
'client_secret' => $this->options->secret,
190-
'code' => $code,
191-
'grant_type' => 'authorization_code',
192-
'redirect_uri' => $this->options->callbackURL,
188+
'code' => $code,
189+
'grant_type' => 'authorization_code',
190+
'redirect_uri' => $this->options->callbackURL,
193191
];
194192

193+
if(!$this::USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST){
194+
$params['client_id'] = $this->options->key;
195+
$params['client_secret'] = $this->options->secret;
196+
}
197+
195198
if($this instanceof PKCE){
196199
$params = $this->setCodeVerifier($params);
197200
}
@@ -215,6 +218,10 @@ protected function sendAccessTokenRequest(string $url, array $body):ResponseInte
215218
$request = $request->withHeader($header, $value);
216219
}
217220

221+
if($this::USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST){
222+
$request = $this->addBasicAuthHeader($request);
223+
}
224+
218225
return $this->http->sendRequest($request);
219226
}
220227

src/Providers/PayPal.php

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313

1414
namespace chillerlan\OAuth\Providers;
1515

16-
use chillerlan\HTTP\Utils\QueryUtil;
1716
use chillerlan\OAuth\Core\{AuthenticatedUser, ClientCredentials, CSRFToken, OAuth2Provider, TokenRefresh, UserInfo};
18-
use Psr\Http\Message\ResponseInterface;
19-
use const PHP_QUERY_RFC1738;
2017

2118
/**
2219
* PayPal OAuth2
@@ -36,40 +33,14 @@ class PayPal extends OAuth2Provider implements ClientCredentials, CSRFToken, Tok
3633
self::SCOPE_EMAIL,
3734
];
3835

36+
public const USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST = true;
37+
3938
protected string $accessTokenURL = 'https://api.paypal.com/v1/oauth2/token';
4039
protected string $authorizationURL = 'https://www.paypal.com/connect';
4140
protected string $apiURL = 'https://api.paypal.com';
4241
protected string|null $applicationURL = 'https://developer.paypal.com/developer/applications/';
4342
protected string|null $apiDocs = 'https://developer.paypal.com/docs/connect-with-paypal/reference/';
4443

45-
/**
46-
* @inheritDoc
47-
*/
48-
protected function getAccessTokenRequestBodyParams(string $code):array{
49-
return [
50-
'code' => $code,
51-
'grant_type' => 'authorization_code',
52-
'redirect_uri' => $this->options->callbackURL,
53-
];
54-
}
55-
56-
/**
57-
* @inheritDoc
58-
*/
59-
protected function sendAccessTokenRequest(string $url, array $body):ResponseInterface{
60-
61-
$request = $this->requestFactory
62-
->createRequest('POST', $url)
63-
->withHeader('Accept-Encoding', 'identity')
64-
->withHeader('Content-Type', 'application/x-www-form-urlencoded')
65-
->withBody($this->streamFactory->createStream(QueryUtil::build($body, PHP_QUERY_RFC1738)))
66-
;
67-
68-
$request = $this->addBasicAuthHeader($request);
69-
70-
return $this->http->sendRequest($request);
71-
}
72-
7344
/**
7445
* @inheritDoc
7546
* @codeCoverageIgnore

src/Providers/Reddit.php

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,11 @@
1313

1414
namespace chillerlan\OAuth\Providers;
1515

16-
use chillerlan\HTTP\Utils\QueryUtil;
1716
use chillerlan\OAuth\Core\{
1817
AccessToken, AuthenticatedUser, ClientCredentials, CSRFToken, OAuth2Interface,
1918
OAuth2Provider, TokenInvalidate, TokenRefresh, UserInfo
2019
};
21-
use Psr\Http\Message\ResponseInterface;
2220
use function sprintf;
23-
use const PHP_QUERY_RFC1738;
2421

2522
/**
2623
* @see https://github.com/reddit-archive/reddit/wiki/OAuth2
@@ -76,6 +73,8 @@ class Reddit extends OAuth2Provider implements ClientCredentials, CSRFToken, Tok
7673
'User-Agent' => self::USER_AGENT,
7774
];
7875

76+
public const USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST = true;
77+
7978
protected string $authorizationURL = 'https://www.reddit.com/api/v1/authorize';
8079
protected string $accessTokenURL = 'https://www.reddit.com/api/v1/access_token';
8180
protected string $apiURL = 'https://oauth.reddit.com/api';
@@ -84,34 +83,6 @@ class Reddit extends OAuth2Provider implements ClientCredentials, CSRFToken, Tok
8483
protected string|null $applicationURL = 'https://www.reddit.com/prefs/apps/';
8584
protected string|null $userRevokeURL = 'https://www.reddit.com/settings/privacy';
8685

87-
/**
88-
* @inheritDoc
89-
*/
90-
protected function getAccessTokenRequestBodyParams(string $code):array{
91-
return [
92-
'code' => $code,
93-
'grant_type' => 'authorization_code',
94-
'redirect_uri' => $this->options->callbackURL,
95-
];
96-
}
97-
98-
/**
99-
* @inheritDoc
100-
*/
101-
protected function sendAccessTokenRequest(string $url, array $body):ResponseInterface{
102-
103-
$request = $this->requestFactory
104-
->createRequest('POST', $url)
105-
->withHeader('Accept-Encoding', 'identity')
106-
->withHeader('Content-Type', 'application/x-www-form-urlencoded')
107-
->withBody($this->streamFactory->createStream(QueryUtil::build($body, PHP_QUERY_RFC1738)))
108-
;
109-
110-
$request = $this->addBasicAuthHeader($request);
111-
112-
return $this->http->sendRequest($request);
113-
}
114-
11586
/**
11687
* @inheritDoc
11788
* @codeCoverageIgnore

tests/Providers/Unit/OAuth2ProviderUnitTestAbstract.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,14 @@ public function testGetAccessTokenRequestBodyParams():void{
187187
$params = $this->invokeReflectionMethod('getAccessTokenRequestBodyParams', ['*test_code*']);
188188

189189
$this::assertSame('*test_code*', $params['code']);
190-
$this::assertSame($this->options->key, $params['client_id']);
191-
$this::assertSame($this->options->secret, $params['client_secret']);
192190
$this::assertSame($this->options->callbackURL, $params['redirect_uri']);
193191
$this::assertSame('authorization_code', $params['grant_type']);
194192

193+
if(!$this->provider::USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST){
194+
$this::assertSame($this->options->key, $params['client_id']);
195+
$this::assertSame($this->options->secret, $params['client_secret']);
196+
}
197+
195198
if($this->provider instanceof PKCE){
196199
$this::assertSame($verifier, $params['code_verifier']);
197200
}
@@ -208,6 +211,13 @@ public function testSendAccessTokenRequest():void{
208211
$this::assertSame($url, $json->request->url);
209212
$this::assertSame('POST', $json->request->method);
210213
$this::assertSame('foo=bar', $json->body);
214+
215+
if($this->provider::USES_BASIC_AUTH_IN_ACCESS_TOKEN_REQUEST){
216+
$authHeader = 'Basic '.base64_encode($this->options->key.':'.$this->options->secret);
217+
218+
$this::assertSame($authHeader, $json->headers->{'Authorization'});
219+
}
220+
211221
}
212222

213223

tests/Providers/Unit/PayPalTest.php

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,4 @@ protected function getProviderFQCN():string{
2424
return PayPal::class;
2525
}
2626

27-
public function testGetAccessTokenRequestBodyParams():void{
28-
$params = $this->invokeReflectionMethod('getAccessTokenRequestBodyParams', ['*test_code*']);
29-
30-
$this::assertSame('*test_code*', $params['code']);
31-
$this::assertSame($this->options->callbackURL, $params['redirect_uri']);
32-
$this::assertSame('authorization_code', $params['grant_type']);
33-
}
34-
35-
public function testSendAccessTokenRequest():void{
36-
$url = 'https://localhost/access_token';
37-
$response = $this->invokeReflectionMethod('sendAccessTokenRequest', [$url, ['foo' => 'bar']]);
38-
$json = MessageUtil::decodeJSON($response);
39-
40-
// paypal uses basic auth instead of the credentials in the body
41-
$authHeader = 'Basic '.base64_encode($this->options->key.':'.$this->options->secret);
42-
43-
$this::assertSame($authHeader, $json->headers->{'Authorization'});
44-
45-
$this::assertSame('identity', $json->headers->{'Accept-Encoding'});
46-
$this::assertSame('application/x-www-form-urlencoded', $json->headers->{'Content-Type'});
47-
$this::assertSame($url, $json->request->url);
48-
$this::assertSame('POST', $json->request->method);
49-
$this::assertSame('foo=bar', $json->body);
50-
}
51-
5227
}

tests/Providers/Unit/RedditTest.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111

1212
namespace chillerlan\OAuthTest\Providers\Unit;
1313

14-
use chillerlan\OAuth\Core\AccessToken;
15-
use chillerlan\OAuth\Core\TokenInvalidate;
14+
use chillerlan\OAuth\Core\{AccessToken, TokenInvalidate};
1615
use chillerlan\OAuth\Providers\Reddit;
1716

1817
/**
@@ -24,14 +23,6 @@ protected function getProviderFQCN():string{
2423
return Reddit::class;
2524
}
2625

27-
public function testGetAccessTokenRequestBodyParams():void{
28-
$params = $this->invokeReflectionMethod('getAccessTokenRequestBodyParams', ['*test_code*']);
29-
30-
$this::assertSame('*test_code*', $params['code']);
31-
$this::assertSame($this->options->callbackURL, $params['redirect_uri']);
32-
$this::assertSame('authorization_code', $params['grant_type']);
33-
}
34-
3526
public function testTokenInvalidate():void{
3627

3728
if(!$this->provider instanceof TokenInvalidate){

0 commit comments

Comments
 (0)