OAUTHnesia for Windows 8 Metro Apps C#

The last few days at Bandung was spent hacking a “proof of concept” application for Windows 8 Metro without using Urbanesia’s OAUTHnesia library. Now the library is done but still needs a few tweaks. All updates of the codes will be live in its Github Gist.

Without further ado, here’s the gist: [gist]

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
/**
* OAUTHnesia for Windows 8 Metro Apps C#
*
* OAUTH v1.0a library for C# to access Urbanesia API
*
* @package OAUTHnesia
* @subpackage Windows 8 Metro Apps C#
* @category Library
* @author Batista R. Harahap <batista@bango29.com>
* @link http://www.bango29.com
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
namespace Urbanesia.Libraries
{
class OAUTHnesia
{
private string
ConsumerKey,
ConsumerSecret,
UserKey,
UserSecret,
SafeEncode,
ApiUri;
public static string
BASE_URL = "http://api1.urbanesia.com/",
OAUTH_SAFE_ENCODE = "1",
OAUTH_NO_SAFE_ENCODE = "0";
public OAUTHnesia(string consumerKey, string consumerSecret, string safeEncode)
{
this.ConsumerKey = consumerKey;
this.ConsumerSecret = consumerSecret;
this.SafeEncode = safeEncode;
}
public OAUTHnesia(string consumerKey, string consumerSecret, string userKey, string userSecret, string safeEncode)
{
this.ConsumerKey = consumerKey;
this.ConsumerSecret = consumerSecret;
this.UserKey = userKey;
this.UserSecret = userSecret;
this.SafeEncode = safeEncode;
}
public async Task<string> Oauth(string uri = "", string post = "", string get = "", bool signWithUserTokens = false)
{
// Set URI
this.ApiUri = uri;
// Default POST body
string _defaultPost = String.Format(
"oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method=HMAC-SHA1&oauth_timestamp={2}"+
"&oauth_version=1.0",
this.ConsumerKey,
GetNonce(),
Utils.GetUnixTimeAsString()
);
// Add User Token Key to default POST body if requested
if (signWithUserTokens == true)
_defaultPost += String.Format("{0}&oauth_token={1}", _defaultPost, this.UserKey);
// Merge default POST with actual POST
if (post == "")
{
post = _defaultPost;
}
else
{
post += "&" + _defaultPost;
}
// Add Safe Encode if requested
if (this.SafeEncode == OAUTH_SAFE_ENCODE)
{
post += "&safe_encode=" + OAUTH_SAFE_ENCODE;
}
// Merge POST + GET and sort the array
string requestify = this.Sortify(post + "&" + get);
// Encode for OAUTH
requestify = EncodeForOauth(requestify);
// Base Signature
string baseSig = GenerateBaseSignature(requestify);
Debug.WriteLine("Base Signature: " + baseSig);
// Signature
string signature = signWithUserTokens == true ?
Utils.HmacSha1(baseSig, String.Format("{0}&{1}", this.ConsumerSecret, this.UserSecret)) :
Utils.HmacSha1(baseSig, String.Format("{0}&", this.ConsumerSecret));
// Build URL
string oauthSig = String.Format("?oauth_signature={0}", signature);
// Replace all %20 to %2B
get = get.Replace("%20", "%2B");
string url = String.Format("{0}{1}{2}&{3}", BASE_URL, ApiUri, oauthSig, get);
return await SendRequest(url, GeneratePostContent(post));
}
private FormUrlEncodedContent GeneratePostContent(string post)
{
string[] sp = post.Split('&');
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach(string vals in sp)
{
string[] temp = vals.Split('=');
dict.Add(temp[0], temp[1]);
}
return new FormUrlEncodedContent(dict);
}
private async Task<string> SendRequest(string url, FormUrlEncodedContent post)
{
HttpClient http = new HttpClient() { MaxResponseContentBufferSize = 256 * 1024 };
HttpResponseMessage response = await http.PostAsync(url, post);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private string GenerateBaseSignature(string s)
{
return String.Format("POST&{0}&{1}", Utils.UrlEncode(BASE_URL + this.ApiUri), Utils.UrlEncode(s));
}
private String EncodeForOauth(string s)
{
string[] sp = s.Split('&');
string stify = "";
for (int i = 0, j = 0, max = sp.Length; i < max; i++)
{
if (j == 1)
stify += "&";
string[] temp = sp[i].Split('=');
stify += Utils.UrlEncode(temp[0]) + "=" + Utils.UrlEncode(temp[1]);
j = 1;
}
return stify;
}
private string Sortify(string s)
{
string[] sp = s.Split('&');
Array.Sort(sp);
string stify = "";
for (int i = 0, j = 0, max = sp.Length; i < max; i++)
{
if (j == 1)
stify += "&";
string[] temp = sp[i].Split('=');
stify += temp[0] + "=" + temp[1];
j = 1;
}
return stify;
}
private string GetNonce()
{
return Utils.Md5("FSoNTivj8iyqucRI6tx88NYICcuyrdVovJMqBwLz" + DateTime.UtcNow.Millisecond);
}
}
}
view raw OAUTHnesia.cs hosted with ❤ by GitHub
using System;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
/**
* OAUTHnesia for Windows 8 Metro Apps C#
*
* OAUTH v1.0a library for C# to access Urbanesia API
*
* @package OAUTHnesia
* @subpackage Windows 8 Metro Apps C#
* @category Library
* @author Batista R. Harahap <batista@bango29.com>
* @link http://www.bango29.com
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
namespace Urbanesia.Libraries
{
class Utils
{
public static string
CONSUMER_KEY = "CONSUMER_KEY",
CONSUMER_SECRET = "CONSUMER_SECRET";
public static string Md5(string str)
{
var alg = HashAlgorithmProvider.OpenAlgorithm("MD5");
IBuffer buff = CryptographicBuffer.ConvertStringToBinary(str, BinaryStringEncoding.Utf8);
var hashed = alg.HashData(buff);
var res = CryptographicBuffer.EncodeToHexString(hashed);
return res;
}
public static string GetUnixTimeAsString()
{
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
return Convert.ToString(ts.TotalSeconds);
}
public static int GetUnixTimeAsInteger()
{
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
return Convert.ToInt32(ts.TotalSeconds);
}
public static long GetUnixTimeAsLong()
{
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
return Convert.ToInt64(ts.TotalSeconds);
}
public static string HmacSha1(string baseString, string keyString)
{
var crypt = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
var buffer = CryptographicBuffer.ConvertStringToBinary(baseString, BinaryStringEncoding.Utf8);
var keyBuffer = CryptographicBuffer.ConvertStringToBinary(keyString, BinaryStringEncoding.Utf8);
var key = crypt.CreateKey(keyBuffer);
var sigBuffer = CryptographicEngine.Sign(key, buffer);
string signature = CryptographicBuffer.EncodeToBase64String(sigBuffer);
return signature;
}
public static string UrlEncode(string s)
{
switch (s)
{
case "+":
return "%2B";
default:
return System.Uri.EscapeDataString(s);
}
}
}
}
view raw Utils.cs hosted with ❤ by GitHub
[/gist]

OAUTHnesia for PHP

It’s 2012 now and Urbanesia is publishing a new OAUTHnesia client for Urbanesia’s API. This time it’s for PHP. Why it took so long to actually finish a PHP version is because we gave up on a third party library that is too complicated to do simple things. So without further ado, the codes are available below.

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* OAUTHnesia for PHP Class
*
* @package OAUTHnesia
* @subpackage PHP
* @category OAUTH Client
* @author Batista R. Harahap <tista@urbanesia.com>
* @link http://www.bango29.com
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
class Oauthnesia {
var $cons_key = "";
var $cons_sec = "";
var $user_key = "";
var $user_sec = "";
var $API_URL = "http://api1.urbanesia.com/";
var $api_uri = "";
function __construct($conf) {
if(empty($conf))
throw new Oauthnesia_Exception("Empty configuration array");
if(empty($conf['consumer_key']))
throw new Oauthnesia_Exception("Please initialize with Consumer Key");
if(empty($conf['consumer_secret']))
throw new Oauthnesia_Exception("Please initialize with Consumer Secret");
if( (empty($conf['user_key']) && !empty($conf['user_secret'])) || (!empty($user_key) && !empty($user_sec)) )
throw new Oauthnesia_Exception("Both User Tokens must be initialized");
$this->API_URL = !empty($conf['api_base_url']) ? $conf['api_base_url'] : $this->API_URL;
$this->cons_key = $conf['consumer_key'];
$this->cons_sec = $conf['consumer_secret'];
$this->user_key = !empty($conf['user_key']) ? $conf['user_key'] : '';
$this->user_sec = !empty($conf['user_secret']) ? $conf['user_secret'] : '';
}
function request($uri = "", $post = "", $get = "", $with_user_key = false) {
if(empty($uri))
throw new Oauthnesia_Exception("API URI must be initialized");
if(!is_string($uri))
throw new Oauthnesia_Exception("API URI must be a String object");
if(!is_string($post))
throw new Oauthnesia_Exception("API POST Requests must be a String object");
if(!is_string($get))
throw new Oauthnesia_Exception("API GET Requests must be a String object");
if(!is_bool($with_user_key))
throw new Oauthnesia_Exception("User Key Signing flag must be boolean");
$this->api_uri = $uri;
// Add default POST requirements for Urbanesia
$def_post = $this->get_default_post($with_user_key);
// Merge POST with default POST
if(empty($post))
$post = $def_post;
else
$post .= "&" . $def_post;
// Encode POST and GET for OAUTH
$post = $this->encode_for_oauth($post);
$get = $this->encode_for_oauth($get);
// Encode the whole GET + POST
$requestify = $this->sortify($post ."&". $get);
// Base Signature
$base_sig = $this->get_base_sig($requestify);
// Sign the request with Consumer/User Secret
$oauth_sig = hash_hmac("SHA1", $base_sig, $this->cons_sec . "&" . $this->user_sec, TRUE);
$oauth_sig = base64_encode($oauth_sig);
$oauth_sig = str_replace("=", "%3D", $oauth_sig);
$oauth_sig = str_replace("+", "%2B", $oauth_sig);
$oauth_sig = "?oauth_signature=" . $oauth_sig;
$url = $this->API_URL . $this->api_uri . $oauth_sig . "&" . $get;
return $this->send_request($url, $post);
}
function xauth($username, $password) {
if(empty($username))
throw new Oauthnesia_Exception("No username specified for xAuth");
if(empty($password))
throw new Oauthnesia_Exception("No password specified for xAuth");
$this->api_uri = "oauth/access_token";
// Add default POST requirements for Urbanesia
$def_post = $this->get_default_post(FALSE);
// Xauth Default POST
$xpost = "&x_auth_username={$username}&x_auth_password={$password}&x_auth_mode=client_auth";
// Merge POST with Xauth
$post = $this->encode_for_oauth($def_post . $xpost);
// Base Signature
$base_sig = $this->get_base_sig($post);
// Sign the request with Consumer/User Secret
$oauth_sig = hash_hmac("SHA1", $base_sig, $this->cons_sec . "&" . $this->user_sec, TRUE);
$oauth_sig = base64_encode($oauth_sig);
$oauth_sig = str_replace("=", "%3D", $oauth_sig);
$oauth_sig = str_replace("+", "%2B", $oauth_sig);
$oauth_sig = "?oauth_signature=" . $oauth_sig;
$url = $this->API_URL . $this->api_uri . $oauth_sig;
$result = $this->send_request($url, $post);
$r =& $result['response'];
if(
!empty($r->result) &&
!empty($r->result->oauth_token_key) &&
!empty($r->result->oauth_token_secret) &&
!empty($r->result->first_name) &&
!empty($r->result->last_name) &&
!empty($r->result->account_id)
) {
return $r->result;
} else {
return FALSE;
}
}
function send_request($url, $post) {
if(empty($url) || empty($post))
throw new Oauthnesia_Exception("Empty URL/POST request for CURL");
$c = curl_init();
curl_setopt($c, CURLOPT_USERAGENT, "OAUTHnesia for PHP v1.0");
curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($c, CURLOPT_TIMEOUT, 5);
curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($c, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_POST, TRUE);
curl_setopt($c, CURLOPT_POSTFIELDS, $post);
$response = curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
$info = curl_getinfo($c);
$mem = memory_get_usage(TRUE);
$return = array(
'response' => json_decode($response),
'code' => $code,
'info' => $info,
'memory' => round($mem/1024/1024, 2) . " MB"
);
return $return;
}
function get_base_sig($req) {
return "POST&" . rawurlencode($this->API_URL . $this->api_uri) . "&"
. rawurlencode($req);
}
function sortify($body) {
$arr = explode("&", $body);
$arr = array_filter($arr);
array_multisort($arr);
return implode("&", $arr);
}
function encode_for_oauth($var) {
$vars = explode("&", $var);
$vars = array_filter($vars);
array_multisort($vars);
$return = "";
for($i=0, $j=0, $max=count($vars); $i<$max; $i++) {
if($j === 1)
$return .= "&";
$tmp = explode("=", $vars[$i]);
$return .= rawurlencode($tmp[0]) . "=" . rawurlencode($tmp[1]);
$j = 1;
}
return $return;
}
function get_default_post($with_user_key = false) {
if($with_user_key === TRUE && empty($this->user_key))
throw new Oauthnesia_Exception("Trying to make a request signed with User Secret without initializing with User Secret");
$post = "oauth_consumer_key=" . $this->cons_key
. "&oauth_nonce=" . $this->get_nonce()
. "&oauth_signature_method=HMAC-SHA1"
. "&oauth_timestamp=" . mktime()
. "&oauth_version=1.0"
. "&safe_encode=1";
if($with_user_key)
return $post . "&oauth_token=" . $this->user_key;
else
return $post;
}
function get_nonce() {
return md5(microtime(TRUE));
}
function safe_encode($data) {
if (is_array($data)) {
return array_map(array($this, 'safe_encode'), $data);
} else if (is_scalar($data)) {
return str_ireplace(
array('+', '%7E'),
array(' ', '~'),
rawurlencode($data)
);
} else {
return '';
}
}
}
class Oauthnesia_Exception extends Exception {}
view raw OAUTHnesia.php hosted with ❤ by GitHub

HMAC-SHA1 Base64 Result With Objective-C

A few days of hard done nights were all inspired by faulty encodings. Talk about “Hello World” experiences LOL. Keeping things in mind, to really smooth things up between Objective-C and PHP, in any Objective-C functions needing to encode/decode strings like PHP, here’s the encoding type: NSASCIIStringEncoding Do not interchange this with any other or things will go wrong :( Here are snippets to generate HMAC-SHA1 hashes in its Base64 form encoded correctly for OAUTH v1. [Read More]

OAUTHnesia Client for Objective-C

Okay for the last few hours, I’ve been learning how to code in Objective-C and the first result is an OAUTH client for Urbanesia. I haven’t tested thoroughly though. The class basically wraps POST & GET requests to Urbanesia with OAUTH requirements. Instantiating the class, you will have to provide Consumer Key & Secret obtained from Urbanesia. The current implementation requires User Key & Secret for every requests too. Future release of OAUTHnesia will include non Use Key/Secret methods and XAUTH. [Read More]