-
-
Save clnt/f629cc71217a822221a03713905f9f7b to your computer and use it in GitHub Desktop.
HERE API returning Signature mismatch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Admin\Geocoding\Headers; | |
use Illuminate\Support\Str; | |
use InvalidArgumentException; | |
use RuntimeException; | |
class OAuth1Header | |
{ | |
public const REQUEST_METHOD_HEADER = 'header'; | |
public const REQUEST_METHOD_QUERY = 'query'; | |
public const SIGNATURE_METHOD_HMAC_1 = 'HMAC-SHA1'; | |
public const SIGNATURE_METHOD_HMAC_256 = 'HMAC-SHA256'; | |
public const SIGNATURE_METHOD_RSA = 'RSA-SHA1'; | |
public const SIGNATURE_METHOD_PLAINTEXT = 'PLAINTEXT'; | |
/** | |
* @var array | |
*/ | |
private $options; | |
/** | |
* @var string | |
*/ | |
protected $method; | |
/** | |
* If post request, pass body contents. | |
* | |
* @var array | |
*/ | |
protected $body; | |
/** | |
* @var string | |
*/ | |
protected $nonce; | |
public function __construct(array $options = [], string $method = 'GET', array $body = []) | |
{ | |
$this->options = array_merge($this->getDefaultOptions(), $options); | |
$this->method = $method; | |
$this->body = $body; | |
$this->nonce = Str::random(32); | |
} | |
public static function make(array $options = [], string $method = 'GET', array $body = []): self | |
{ | |
return new self($options, $method, $body); | |
} | |
/** | |
* Returns the built OAuth 1.0 header as a query string | |
* or Authorization header value. | |
*/ | |
public function build(): string | |
{ | |
$parameters = $this->buildParameters(); | |
if ($this->getRequestMethod() === self::REQUEST_METHOD_HEADER) { | |
return $this->buildAuthorizationHeader($parameters); | |
} | |
if ($this->getRequestMethod() === self::REQUEST_METHOD_QUERY) { | |
return $this->buildQuery($parameters); | |
} | |
throw new InvalidArgumentException('Invalid request_method option: ' . $this->getRequestMethod()); | |
} | |
private function buildAuthorizationHeader(array $params): string | |
{ | |
foreach ($params as $key => $value) { | |
if (array_key_exists($key, $this->body)) { | |
unset($params[$key]); | |
continue; | |
} | |
$params[$key] = $key . '="' . rawurlencode($value) . '"'; | |
} | |
// Replicate same order as Postman | |
$params = [ | |
$params['oauth_consumer_key'], | |
$params['oauth_signature_method'], | |
$params['oauth_timestamp'], | |
$params['oauth_nonce'], | |
$params['oauth_version'], | |
$params['oauth_signature'], | |
]; | |
return 'OAuth ' . implode(',', $params); | |
} | |
private function buildQuery(array $params): string | |
{ | |
return http_build_query($params, '', '&', PHP_QUERY_RFC3986); | |
} | |
private function buildParameters(): array | |
{ | |
$params = $this->generateOAuthParameters(); | |
if ($this->method === 'POST' && count($this->body) > 0) { | |
$params = array_merge($params, $this->body); | |
} | |
return tap($this->generateSignature($params), static function (array &$params): void { | |
uksort($params, 'strcmp'); | |
}); | |
} | |
private function generateOAuthParameters(): array | |
{ | |
$params = [ | |
'oauth_consumer_key' => $this->options['consumer_key'], | |
'oauth_signature_method' => $this->getSignatureMethod(), | |
'oauth_timestamp' => now()->timestamp, | |
'oauth_nonce' => $this->nonce, | |
]; | |
$this->applyOptionalParameters($params); | |
return $params; | |
} | |
private function applyOptionalParameters(array &$params): void | |
{ | |
$optionalParams = [ | |
'callback' => 'oauth_callback', | |
'token' => 'oauth_token', | |
'verifier' => 'oauth_verifier', | |
'version' => 'oauth_version', | |
]; | |
foreach ($optionalParams as $optionName => $oauthName) { | |
if (isset($this->options[$optionName]) === false) { | |
continue; | |
} | |
$params[$oauthName] = $this->options[$optionName]; | |
} | |
} | |
private function generateSignature(array $params): array | |
{ | |
$baseString = $this->generateBaseString($params); | |
if (in_array($this->getSignatureMethod(), [ | |
self::SIGNATURE_METHOD_HMAC_1, | |
self::SIGNATURE_METHOD_HMAC_256, | |
], true) | |
) { | |
return array_merge($params, [ | |
'oauth_signature' => $this->signUsingHmac($this->getSignatureMethod(), $baseString), | |
]); | |
} | |
if ($this->getSignatureMethod() === self::SIGNATURE_METHOD_RSA) { | |
return array_merge($params, [ | |
'oauth_signature' => $this->signUsingRsa($baseString), | |
]); | |
} | |
return array_merge($params, [ | |
'oauth_signature' => $baseString, | |
]); | |
} | |
private function generateBaseString(array $params): string | |
{ | |
return strtoupper($this->method) | |
. '&' . rawurlencode($this->buildQuery($params)); | |
} | |
private function signUsingHmac(string $algorithm, string $baseString): string | |
{ | |
$secret = rawurlencode($this->options['consumer_secret']); | |
if (isset($this->options['token_secret'])) { | |
$secret .= '&' . rawurlencode($this->options['token_secret']); | |
} | |
return base64_encode( | |
hash_hmac( | |
$algorithm === self::SIGNATURE_METHOD_HMAC_1 ? 'sha1' : 'sha256', | |
$baseString, | |
$secret, | |
true | |
) | |
); | |
} | |
private function signUsingRsa(string $baseString): string | |
{ | |
if (!function_exists('openssl_pkey_get_private')) { | |
throw new RuntimeException('RSA-SHA1 signature method ' | |
. 'requires the OpenSSL extension.'); | |
} | |
$privateKey = openssl_pkey_get_private( | |
file_get_contents($this->options['private_key_file']), | |
$this->options['private_key_passphrase'] | |
); | |
$signature = ''; | |
openssl_sign($baseString, $signature, $privateKey); | |
openssl_free_key($privateKey); | |
return $signature; | |
} | |
private function getDefaultOptions(): array | |
{ | |
return [ | |
'version' => '1.0', | |
'request_method' => self::REQUEST_METHOD_HEADER, | |
'consumer_key' => 'anonymous', | |
'consumer_secret' => 'anonymous', | |
'signature_method' => self::SIGNATURE_METHOD_HMAC_256, | |
]; | |
} | |
private function getRequestMethod(): ?string | |
{ | |
return $this->options['request_method'] ?? null; | |
} | |
private function getSignatureMethod(): ?string | |
{ | |
return $this->options['signature_method'] ?? null; | |
} | |
public function getOptions(): array | |
{ | |
return $this->options; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment