Skip to content

Instantly share code, notes, and snippets.

@ahamilton9
Created May 19, 2021 18:22
Show Gist options
  • Save ahamilton9/9eb5de9c3fad2fc29ba76e67ac2cda59 to your computer and use it in GitHub Desktop.
Save ahamilton9/9eb5de9c3fad2fc29ba76e67ac2cda59 to your computer and use it in GitHub Desktop.
Generates the Authorization header for OAuth 1.0 signed requests
<?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