Created
May 19, 2021 18:22
-
-
Save ahamilton9/9eb5de9c3fad2fc29ba76e67ac2cda59 to your computer and use it in GitHub Desktop.
Generates the Authorization header for OAuth 1.0 signed requests
This file contains hidden or 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\Services; | |
use GuzzleHttp\Psr7\Request; | |
/** | |
* Generates the Authorization header for OAuth 1.0 signed requests | |
* | |
* Heavily based off of the request signing code in Abraham\TwitterOAuth. Built generically, but | |
* currently only contains the SHA1 hashing code for Twitter. Should be reasonable to extend. | |
* | |
* Example usage: | |
* \App\Services\OAuthRequestSigningService::signRequest( | |
* $request, | |
* $formParams, | |
* 'HMAC-SHA1', | |
* $twitterAppKey, | |
* $twitterAppSecret, | |
* $userToken, | |
* $userTokenSecret | |
* ); | |
*/ | |
class OAuthRequestSigningService { | |
/** | |
* Get the Authorization header for an OAuth 1.0 signed request | |
* | |
* @param Request $request | |
* @param array $params | |
* @param string $signatureHashingAlgo | |
* @param string $appKey | |
* @param string $appSecret | |
* @param string $token | |
* @param string $tokenSecret | |
* @return string | |
*/ | |
static public function signRequest( | |
Request $request, | |
array $params = [], | |
string $signatureHashingAlgo, | |
string $appKey = null, | |
string $appSecret = null, | |
string $token = null, | |
string $tokenSecret = null | |
) { | |
$algo = self::solveAlgo($signatureHashingAlgo); | |
self::validate($signatureHashingAlgo, $appKey, $appSecret, $token, $tokenSecret, $algo); | |
$params = self::setOAuthParams($signatureHashingAlgo, $appKey, $token, $params); | |
$normalizedUrl = self::normalizeUrl($request); | |
$signatureBase = self::buildSignatureBase($request, $params, $normalizedUrl); | |
$signingKey = self::buildSigningKey($appSecret, $token, $tokenSecret); | |
$params = self::buildSignature($params, $algo, $signatureBase, $signingKey); | |
return self::buildHeader($params); | |
} | |
/** | |
* Get the algorithm name for the HMAC hashing function | |
* | |
* @param string $signatureHashingAlgo | |
* @return string | |
*/ | |
static private function solveAlgo(string $signatureHashingAlgo) { | |
$algo = null; | |
switch($signatureHashingAlgo) { | |
case 'HMAC-SHA1': | |
$algo = 'sha1'; | |
} | |
return $algo; | |
} | |
/** | |
* Check Request validity | |
* | |
* @param string $appKey | |
* @param string $appSecret | |
* @param string $token | |
* @param string $tokenSecret | |
* @param string $algo | |
* @return void | |
*/ | |
static private function validate( | |
string $appKey, | |
string $appSecret, | |
string $token, | |
string $tokenSecret, | |
string $algo | |
) { | |
$algos = hash_hmac_algos(); | |
if(! in_array($algo, $algos)) {throw new \Exception('Invalid hashing algo provided');} | |
if(is_null($appKey)) {throw new \Exception('No app key provided');} | |
if(is_null($appSecret)) {throw new \Exception('No app secret provided');} | |
if(is_null($token)) {throw new \Exception('No token provided');} | |
if(is_null($tokenSecret)) {throw new \Exception('No token secret provided');} | |
} | |
/** | |
* Add OAuth parameters to list | |
* | |
* @param string $signatureHashingAlgo | |
* @param string $appKey | |
* @param string $token | |
* @param array $params | |
* @return array | |
*/ | |
static private function setOAuthParams( | |
string $signatureHashingAlgo, | |
string $appKey, | |
string $token, | |
array $params | |
) { | |
$defaults = [ | |
'oauth_version' => '1.0', | |
'oauth_nonce' => md5(microtime() . mt_rand()), | |
'oauth_timestamp' => time(), | |
'oauth_consumer_key' => $appKey, | |
'oauth_token' => $token, | |
'oauth_signature_method' => $signatureHashingAlgo | |
]; | |
$params = array_merge($params, $defaults); | |
ksort($params); | |
return $params; | |
} | |
/** | |
* Normalize the Request URL | |
* | |
* @param Request $request | |
* @return string | |
*/ | |
static private function normalizeUrl(Request $request) { | |
$uri = $request->getUri(); | |
$scheme = $uri->getScheme(); | |
$host = strtolower($uri->getHost()); | |
$path = $uri->getPath(); | |
return "$scheme://$host$path"; | |
} | |
/** | |
* Build signature base string | |
* | |
* @param Request $request | |
* @param array $params | |
* @param string $normalizedUrl | |
* @return string | |
*/ | |
static private function buildSignatureBase( | |
Request $request, | |
array $params, | |
string $normalizedUrl | |
) { | |
$parts = [ | |
rawurlencode(strtoupper($request->getMethod())), | |
rawurlencode($normalizedUrl), | |
rawurlencode(http_build_query($params, '', '&', PHP_QUERY_RFC3986)), | |
]; | |
return implode('&', $parts); | |
} | |
/** | |
* Build signature signing key | |
* | |
* @param string $appSecret | |
* @param string $token | |
* @param string $tokenSecret | |
* @return string | |
*/ | |
static private function buildSigningKey( | |
string $appSecret, | |
string $token, | |
string $tokenSecret | |
) { | |
$parts = [ | |
rawurlencode($appSecret), | |
null !== $token ? rawurlencode($tokenSecret) : '' | |
]; | |
return implode('&', $parts); | |
} | |
/** | |
* Build the final OAuth Signature | |
* | |
* @param array $params | |
* @param string $algo | |
* @param string $signatureBase | |
* @param string $signingKey | |
* @return array | |
*/ | |
static private function buildSignature( | |
array $params, | |
string $algo, | |
string $signatureBase, | |
string $signingKey | |
) { | |
$signature = base64_encode(hash_hmac( | |
$algo, | |
$signatureBase, | |
$signingKey, | |
true | |
)); | |
$params['oauth_signature'] = $signature; | |
return $params; | |
} | |
/** | |
* Build the OAuth Authorization Header | |
* | |
* @param array $params | |
* @return string | |
*/ | |
static private function buildHeader(array $params) { | |
$first = true; | |
$header = 'OAuth'; | |
foreach($params as $k => $v) { | |
if(substr($k, 0, 5) != 'oauth') { | |
continue; | |
} | |
if(is_array($v)) { | |
throw new \Exception( | |
'Arrays not supported in headers', | |
); | |
} | |
$header .= $first ? ' ' : ', '; | |
$header .= | |
rawurlencode($k) . | |
'="' . | |
rawurlencode($v) . | |
'"'; | |
$first = false; | |
} | |
return $header; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment