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); | |
} | |
} | |
} |
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); | |
} | |
} | |
} | |
} |