TracNav
oAuth Example & code
Most methods contain at least one example call and result on each method page. Some Hyves-API library developers have added some example applications so you can learn from them.
oAuth authentication example
Obtaining an Unauthorized Request Token
Before authentication of an user can proceed, the Consumer first has to retrieve an unauthorized requesttoken. An unauthorized requesttoken is retrieved using the api-method auth.requesttoken.
Example with default expirationtype:
url
http://data.hyves-api.nl/?ha_fancylayout=false&ha_format=xml&ha_method=auth.requesttoken&ha_version=1.2&methods=users.get%2Cfriends.get&oauth_consumer_key=MjAzX6ij-hAFyaDCoZCzVDDMJkU%3D&oauth_nonce=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1203341670&oauth_version=1.0&oauth_signature=8qJA%2BF4BzN7qF9Lq1ykUxHuFe98%3D
result
<?xml version="1.0" encoding="UTF-8"?> <auth_requesttoken_result> <oauth_token>cnRfMjAzXwQCAAAAAAAAAAFfMTIwMzM0MTcxNF91NDg2NTY2MzRfNEKimM4cyq8jI_HOR05hTQ==</oauth_token> <oauth_token_secret>hrTGEvDRP7FK1zQkPzj1ow==</oauth_token_secret> <info> <timestamp_difference>1</timestamp_difference> <running_milliseconds>12</running_milliseconds> </info> </auth_requesttoken_result>
Both oauth_token and oauth_token_secret should be saved at the Consumer for this session. Now proceed to Obtaining User Authorization.
Obtaining User Authorization
Now the Consumer must let the User authorize the requesttoken before the requesttoken is of any value. This is done by redirecting the User to a certain url where the User can allow access of the Consumer. The authorize url expects "oauth_token" and "oauth_callback" as parameters.
Example:
url
http://www.hyves.nl/api/authorize/?oauth_token=cnRfMjAzXwQCAAAAAAAAAAFfMTIwMzM0MTcxNF91NDg2NTY2MzRfNEKimM4cyq8jI_HOR05hTQ%3D%3D&oauth_callback=http%3A%2F%2Fwww.consumer.com%2Fauthorized%3Fidentification%3Dabcdef
let the User log-in or register (skipped when the user has auto-login on Hyves)
User allows access with expirationtype = 'default' and 2 methods (users.get and friends.get)
Note: "Hyves Test Application" is the name of the consumer filled at signup. The link points the application website.
User allows access with expirationtype = 'infinite' and 2 methods (users.get and friends.get)
User allows access with expirationtype = 'user' and 2 methods (users.get and friends.get)
User allows access on mobile device (either by user-agent detection or by using http://www.hyves.nl/mini/api/authorize/ )
User get redirected to oauth_callback with requesttoken as parameter
http://www.consumer.com/authorized?identification=abcdef&oauth_token=cnRfMjAzXwQCAAAAAAAAAAFfMTIwMzM0MTcxNF91NDg2NTY2MzRfNEKimM4cyq8jI_HOR05hTQ==
If the User accepted or declined the requesttoken, and an oauth_callback is given, the User will be redirected to the oauth_callback. When redirected the Consumer should proceed doing the next step (Obtaining an Access Token).
When no oauth_callback is given, Hyves will present the User with the following webpage:
Obtaining an Access Token
The Consumer needs to exchange the authorized requesttoken for an accesstoken. This is done with the api-method auth.accesstoken.
Example with authorized requesttoken:
url
http://data.hyves-api.nl/?ha_fancylayout=false&ha_format=xml&ha_method=auth.accesstoken&ha_version=1.2&oauth_consumer_key=MTIwX15yKbjOBmsSakUHe9yjdW0%3D&oauth_nonce=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1203342566&oauth_token=cnRfMTIwXwQCAAAAAAAAAABfMTIwMzM0MjQ3NF84NzA2MjE0MTlft4qqNRAdF4ZpQvTXmn1vJQ%3D%3D&oauth_version=1.0&oauth_signature=sUnvyh83xz5SoO12A32PrbWrfL0%3D
Note that the signature is now also based on the oauth_token_secret!
result
<?xml version="1.0" encoding="UTF-8"?> <auth_accesstoken_result> <oauth_token>YXRfMjQ4Ml_eJjDPdAXlwKR-rXTbiMbe</oauth_token> <oauth_token_secret>zk84NVs-kX-nGGfT2RoHDg==</oauth_token_secret> <userid>45e2b041d9387ff1</userid> <methods>users.get,friends.get</methods> <expiredate>1205934567</expiredate> <info> <timestamp_difference>1</timestamp_difference> <running_milliseconds>32</running_milliseconds> </info> </auth_accesstoken_result>
Example with declined requesttoken:
url
http://data.hyves-api.nl/?ha_fancylayout=false&ha_format=xml&ha_method=auth.accesstoken&ha_version=1.2&oauth_consumer_key=MTIwX15yKbjOBmsSakUHe9yjdW0%3D&oauth_nonce=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1203343338&oauth_token=cnRfMTIwXwQCAAAAAAAAAABfMTIwMzM0MjgyOV8xMzQxNjA0ODQ5X9AE7zlnRIouPE15L6Qpz-w%3D&oauth_version=1.0&oauth_signature=r20wqP9K4pMq0qYiGK42RmkVGrk%3D
result
<?xml version="1.0" encoding="UTF-8"?> <error_result> <error_code>32</error_code> <error_message>The requesttoken used to obtain an accesstoken was declined by the user</error_message> <request_parameters> <parameter> <requestkey>ha_fancylayout</requestkey> <requestvalue>false</requestvalue> </parameter> etc...
Example with not yet accepted requesttoken:
url
http://data.hyves-api.nl/?ha_fancylayout=false&ha_format=xml&ha_method=auth.accesstoken&ha_version=1.2&oauth_consumer_key=MTIwX15yKbjOBmsSakUHe9yjdW0%3D&oauth_nonce=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1203343338&oauth_token=cnRfMTIwXwQCAAAAAAAAAABfMTIwMzM0MjgyOV8xMzQxNjA0ODQ5X9AE7zlnRIouPE15L6Qpz-w%3D&oauth_version=1.0&oauth_signature=r20wqP9K4pMq0qYiGK42RmkVGrk%3D
result
<?xml version="1.0" encoding="UTF-8"?> <error_result> <error_code>31</error_code> <error_message>The requesttoken used to obtain an accesstoken was not authorized</error_message> <request_parameters> <parameter> <requestkey>ha_fancylayout</requestkey> <requestvalue>false</requestvalue> </parameter> etc...
Note that accesstokens are authorization communication between the Consumer and Service Provider. Therefor accesstokens should never be exposed to the User in any way (cookie/url/javascript). The Consumer must implement another identification method to match User's to their accesstokens.
User revokes an Accesstoken
The user is able to remove any authorized accesstokens at http://www.hyves.nl/profilemanage/external/api/
When the user revokes an accesstoken and the Consumer has listeners registered, a callback will be done (read more).
Api privacy page on hyves:
oAuth signing
(the code below is available in the attachment files at the bottom of the page)
One of the most error-prone areas of oAuth is calculating the secret. The examples below are from the oAuth Testcases page; the code samples are hereby released into the public domain under an MIT license. All code runs under php 5.2.1.
urlencodeRFC3986_UTF8 and urldecodeRFC3986_UTF8
Two functions needed to do correct encoding and decoding. Note: we do not test the character that is not in Latin-1, since we don't support non-latin-1.
Tests:
function testEncoding() {
$aEncodingsToTest = array(
array("src"=>"abcABC123", "target"=>"abcABC123"),
array("src"=>"-._~", "target"=>"-._~"),
array("src"=>"%", "target"=>"%25"),
array("src"=>"&=*", "target"=>"%26%3D%2A"),
array("src"=>"\n", "target"=>"%0A"),
array("src"=>" ", "target"=>"%20"),
array("src"=>"\x7F", "target"=>"%7F"),
array("src"=>"\x80", "target"=>"%C2%80"),
);
foreach($aEncodingsToTest as $item) {
$processedsrc = OAuthUtil::urlencodeRFC3986_UTF8($item["src"]);
$this->assertEqual($processedsrc, $item["target"]);
$processedtarget = OAuthUtil::urldecodeRFC3986_UTF8($item["target"]);
$this->assertEqual($processedtarget, $item["src"]);
}
}
Code (note: instead of iconv, utf8_encode and utf8_decode can be used, however, those functions silently fail on unknown conversions):
public static function urlencodeRFC3986($string) {
return str_replace('%7E', '~', rawurlencode($string));
}
public static function urlencodeRFC3986_UTF8($string) {
return OAuthUtil::urlencodeRFC3986(iconv("ISO-8859-1", "UTF-8", $string));
}
public static function urldecodeRFC3986($string) {
return rawurldecode($string); // no exta stuff needed for ~, goes correctly automatically
}
public static function urldecodeRFC3986_UTF8($string) {
return iconv("UTF-8", "ISO-8859-1", OAuthUtil::urldecodeRFC3986($string));
}
normalizeParameters
Normalized request parameters as in oAuth specification section 9.1.1.
Tests:
function testNormalisation() {
$aNormalisationToTest = array(
array("src"=>array(
array("key"=>"name", "value"=>""),
) , "target"=>"name="),
array("src"=>array(
array("key"=>"a", "value"=>"b"),
) , "target"=>"a=b"),
array("src"=>array(
array("key"=>"a", "value"=>"b"),
array("key"=>"c", "value"=>"d"),
) , "target"=>"a=b&c=d"),
array("src"=>array(
array("key"=>"a", "value"=>"x!y"),
array("key"=>"a", "value"=>"x y"),
) , "target"=>"a=x%20y&a=x%21y"),
array("src"=>array(
array("key"=>"x!y", "value"=>"a"),
array("key"=>"x", "value"=>"a"),
) , "target"=>"x=a&x%21y=a"),
);
foreach($aNormalisationToTest as $item) {
$processedsrc = OAuthUtil::normalizeParameters($item["src"]);
$this->assertEqual($processedsrc, $item["target"]);
}
}
Code:
public static function normalizeParameters($aParam) {
$sortfunc = create_function('$a, $b', '
$cmp = strcmp($a["key"], $b["key"]);
if ($cmp == 0) {
return strcmp($a["value"], $b["value"]);
}
return $cmp;
');
usort($aParam, $sortfunc);
$aEncodedVars = array();
foreach($aParam as $param) {
$aEncodedVars[] = self::urlencodeRFC3986_UTF8($param["key"])."=".self::urlencodeRFC3986_UTF8($param["value"]);
}
return implode("&", $aEncodedVars);
}
generateBaseString
Normalized request parameters as in oAuth specification section 9.1.3. Note: We don't follow the exact Testcases, since they are not ordered yet, we assume the normalizeParameters function took care of ordening.
Tests:
function testBasestringGenerator() {
$aBasestringToTest = array(
array(
"src"=>array(
"http_method"=>"GET",
"uri"=>"http://example.com",
"params"=> "n=v"
),
"target"=>"GET&http%3A%2F%2Fexample.com&n%3Dv"),
array(
"src"=>array(
"http_method"=>"POST",
"uri"=>"https://photos.example.net/request_token",
"params"=> "oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=hsu94j3884jdopsl&oauth_signature_method=PLAINTEXT&oauth_timestamp=1191242090&oauth_version=1.0"
),
"target"=>"POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884jdopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestamp%3D1191242090%26oauth_version%3D1.0"),
array(
"src"=>array(
"http_method"=>"GET",
"uri"=>"http://photos.example.net/photos",
"params"=> "file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original"
),
"target"=>"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.1%26size%3Doriginal"),
);
foreach($aBasestringToTest as $item) {
$processedsrc = OAuthUtil::generateBaseString($item["src"]["http_method"],$item["src"]["uri"],$item["src"]["params"]);
$this->assertEqual($processedsrc, $item["target"]);
}
}
Code:
public static function generateBaseString($http_method, $uri, $params) {
$aBasestring = array(
self::urlencodeRFC3986_UTF8($http_method),
self::urlencodeRFC3986_UTF8($uri),
self::urlencodeRFC3986_UTF8($params),
);
return implode("&", $aBasestring);
}
calculateHMACSHA1Signature
Calculate the HMAC-SHA1 signature.
Tests:
function testHMACSHA1SecretCalculator() {
$aSecretToTest = array(
array(
"src"=>array(
"basestring"=>"bs",
"consumersecret"=>"cs",
"tokensecret"=>"",
),
"target"=>"egQqG5AJep5sJ7anhXju1unge2I="),
array(
"src"=>array(
"basestring"=>"bs",
"consumersecret"=>"cs",
"tokensecret"=>"ts",
),
"target"=>"VZVjXceV7JgPq/dOTnNmEfO0Fv8="),
array(
"src"=>array(
"basestring"=>"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.1%26size%3Doriginal",
"consumersecret"=>"kd94hf93k423kf44",
"tokensecret"=>"pfkkdhi9sl3r4s00",
),
"target"=>"tR3+Ty81lMeYAr/Fid0kMTYa/WM="),
);
foreach($aSecretToTest as $item) {
$processedsrc = OAuthUtil::calculateHMACSHA1Signature($item["src"]["basestring"],$item["src"]["consumersecret"],$item["src"]["tokensecret"]);
$this->assertEqual($processedsrc, $item["target"]);
}
}
Code (Note: PHP has an internal function for SHA1-HMAC if the correct compiler-options were used, below, is a fuction that will work regardless of compiler options):
public static function calculateHMACSHA1Signature($basestring, $consumersecret, $tokensecret) {
$aKey = array(
self::urlencodeRFC3986_UTF8($consumersecret),
self::urlencodeRFC3986_UTF8($tokensecret),
);
$key = implode("&", $aKey);
if (function_exists("hash_hmac")) {
$signature = base64_encode(hash_hmac("sha1", $basestring, $key, true));
} else {
$signature = base64_encode(OAuthUtil::hmacsha1($key, $basestring));
}
return $signature;
}
/**
* HMAC-SHA1 not dependent on php compile flags
**/
public static function hmacsha1($key,$data) {
$blocksize=64;
$hashfunc='sha1';
if (strlen($key)>$blocksize)
$key=pack('H*', $hashfunc($key));
$key=str_pad($key,$blocksize,chr(0x00));
$ipad=str_repeat(chr(0x36),$blocksize);
$opad=str_repeat(chr(0x5c),$blocksize);
$hmac = pack(
'H*',$hashfunc(
($key^$opad).pack(
'H*',$hashfunc(
($key^$ipad).$data
)
)
)
);
return $hmac;
}
Bringing it all together
Now we'll see if we can do it all together. The first test is from the oAuth specification appendix A, the second one we calculated ourselves, so at least we'll know when something will start working different from before. We do expect it to be correct though. Note: this function has a slightly different way to pass parameters into. It makes it impossible for one parameter to be present more than once, but I see no practical situation where a parameter is present more than once.
Tests:
function testCalculateSecret() {
$sVar = array(
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original",
);
$http_method="GET";
$uri="http://photos.example.net/photos";
$oauth_token_secret="pfkkdhi9sl3r4s00";
$consumer_secret="kd94hf93k423kf44";
$signature="tR3+Ty81lMeYAr/Fid0kMTYa/WM=";
hyves_require_once("UTIL/ApiUtil.php");
$calculatedsignature = ApiUtil::calculateOAuthSignature($http_method, $uri, $sVar, $consumer_secret, $oauth_token_secret);
$this->assertEqual($calculatedsignature, $signature, "Calculated and known signature are equal (calculated: ".$calculatedsignature.", known: ".$signature.") ");
}
/**
* Uses the example from the oAuth library spec (http://oauth.net/core/1.0/) to check whether the calculateSecret functionality works correctly
*
*/
function testCalculateSecretWithStrangeChars() {
$sVar = array(
"oauth_consumer_key" => 'a+ %20aa'."\x82",
"oauth_token" => '\$_-.a()\"!a'."\x83",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "''\x84",
"oauth_version" => "1.0",
);
$http_method="GET";
$uri="http://photos.example.net/photos";
$oauth_token_secret="aaaa\x85";
$consumer_secret="aaaa\x86";
$signature="k6MWWnPAg0xqvO/utFCxVNxgGjM=";
hyves_require_once("UTIL/ApiUtil.php");
$calculatedsignature = ApiUtil::calculateOAuthSignature($http_method, $uri, $sVar, $consumer_secret, $oauth_token_secret);
$this->assertEqual($calculatedsignature, $signature, "Calculated and known signature are equal (calculated: ".$calculatedsignature.", known: ".$signature.") ");
}
Code:
/**
* Calculates the secret to be used with oauth
* @param string $http_method: either GET or POST (or any other HTTP method)
* @param string $uri: the request URI (eg: http://www.hyves.nl/methods1), without the query string
* @param array $sVar: key=>value of all the fields important for the secret
* @param string consumersecret: the secret of the consumer
* @param string oauth_token_secret: the token secret. Set to "" if no oauth token present
*
* @return string The calculated secret
**/
public static function calculateOAuthSignature($http_method, $uri, $sVar, $consumersecret, $oauth_token_secret) {
hyves_require_once("UTIL/OAuthUtil.php");
$aParam = array();
foreach($sVar as $key=>$value) {
$aParam[] = array("key"=>$key, "value"=>$value);
}
$params = OAuthUtil::normalizeParameters($aParam);
$basestring = OAuthUtil::generateBaseString($http_method, $uri, $params);
$signature = OAuthUtil::calculateHMACSHA1Signature($basestring, $consumersecret, $oauth_token_secret);
return $signature;
}
Attachments
- authorize-default.png (114.1 KB) - added by kilian 8 weeks ago.
- authorize-infinite.png (115.5 KB) - added by kilian 8 weeks ago.
- authorize-login.png (92.9 KB) - added by kilian 8 weeks ago.
- authorize-successful.png (90.3 KB) - added by kilian 8 weeks ago.
- authorize-user.png (115.7 KB) - added by kilian 8 weeks ago.
- profile-settings-api.png (165.3 KB) - added by kilian 8 weeks ago.
- authorize-mobile.png (25.8 KB) - added by kilian 8 weeks ago.




