diff --git a/src/Http/Auth/OAuth1Authenticator.php b/src/Http/Auth/OAuth1Authenticator.php new file mode 100644 index 00000000..282e59d0 --- /dev/null +++ b/src/Http/Auth/OAuth1Authenticator.php @@ -0,0 +1,71 @@ +generateOAuthHeader($pendingRequest); + $pendingRequest->headers() + ->add('Authorization', $oauthHeader); + } + + private function generateOAuthHeader(PendingRequest $pendingRequest): string + { + $method = $pendingRequest->getMethod(); + $url = $pendingRequest->getUrl(); + $params = array_merge($pendingRequest->query()?->all() ?? [], $pendingRequest->body()?->all() ?? []); + + $oauthParams = [ + 'oauth_consumer_key' => $this->consumerKey, + 'oauth_token' => $this->token, + 'oauth_nonce' => $this->generateNonce(), + 'oauth_timestamp' => time(), + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_version' => '1.0', + ]; + $baseString = $this->generateBaseString($method, $url, array_merge($params, $oauthParams)); + $signature = $this->generateSignature($baseString); + $oauthParams['oauth_signature'] = $signature; + + return 'OAuth '.urldecode(http_build_query($oauthParams, '', ', ')); + + } + + private function generateBaseString(Method $method, string $url, array $params): string + { + ksort($params); + + $query = http_build_query($params, '', '&', PHP_QUERY_RFC3986); + + return strtoupper($method->value).'&'.rawurlencode($url).'&'.rawurlencode($query); + } + + private function generateSignature(string $baseString): string + { + $key = rawurlencode($this->consumerSecret).'&'.rawurlencode($this->tokenSecret); + + return base64_encode(hash_hmac('sha1', $baseString, $key, true)); + } + + private function generateNonce(): string + { + return bin2hex(random_bytes(16)); + } +} diff --git a/tests/Unit/AuthenticatorTest.php b/tests/Unit/AuthenticatorTest.php index 8360bc9e..7818185c 100644 --- a/tests/Unit/AuthenticatorTest.php +++ b/tests/Unit/AuthenticatorTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Saloon\Http\Auth\OAuth1Authenticator; use Saloon\Http\PendingRequest; use Saloon\Http\Faking\MockClient; use Saloon\Http\Faking\MockResponse; @@ -66,6 +67,18 @@ connector()->send($request, $mockClient); }); +test('you can use the oauth1 authenticator', function () { + $request = new UserRequest(); + $request->authenticate(new OAuth1Authenticator('consumer-key', 'consumer-secret', 'token', 'token-secret')); + + $pendingRequest = connector()->createPendingRequest($request); +// + expect($pendingRequest->headers()->get('Authorization')) + ->toContain('consumer-key') + ->toContain('token') + ->toContain('oauth_signature'); +}); + test('you can use your own authenticators', function () { $request = new UserRequest(); $request->authenticate(new PizzaAuthenticator('Margherita', 'San Pellegrino'));