Reputation: 342
I'm porting an app from Android to Windows Phone 8.1. I need to fetch some data that needs an OAUTh authentication. I did some research and I think i need a 0-legged OAuth authentication, because i only have a key & secret and the url to get the data.
In the Android project the code is like this (JAVA):
public JSONObject fetchData() {
try {
OAuthConsumer consumer = new DefaultOAuthConsumer(this.context.getString(R.string.consumer_key), this.context.getString(R.string.consumer_secret));
URL fullURL = new URL(url + "&start=" + start + "");
HttpURLConnection request = (HttpURLConnection) fullURL.openConnection();
consumer.sign(request);
request.setConnectTimeout(15000);
request.setRequestMethod("GET");
request.setRequestProperty("Accept", "application/json");
request.setRequestProperty("Accept-Encoding", "gzip");
request.connect();
switch (request.getResponseCode()) {
case 200:
case 201:
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
br.close();
return new JSONObject(sb.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
My code that i have in Windows phone 8.1 project is (C#):
public async Task<List<UiTEvent>> FetchEvents(ZoekResultatenExtras zre, double locationLatitude, double locationLongitude)
{
String completeURL;
List<UiTEvent> events = new List<UiTEvent>();
if (zre.CurrentLocation)
{
completeURL = SEARCH_EVENTS_URL + zre.SearchQuery + "&pt=" + locationLatitude + "," + locationLongitude + "";
}
else
{
completeURL = SEARCH_EVENTS_URL + zre.SearchQuery;
}
Debug.WriteLine(completeURL);
using (HttpClient client = new HttpClient())
{
OAuthBase oauth = new OAuthBase();
string normalizedUrl;
string normalizedqueryparameters;
var key = "MY_KEY";
var secret = "MY_SECRET";
var URL = new Uri("http://www.uitid.be/uitid/rest/searchv2/search?fq=type%3Aevent&fq=language%3Anl&group=event&rows=15&q=*%3A*&sfield=physical_gis&sort=geodist%28%29+asc&d=10&datetype=today&fq=-category_id%3A0.3.1.0.0&pt=51.0554827,3.7407583&start=0");
var oauth_nonce = oauth.GenerateNonce();
var oauth_timestamp = oauth.GenerateTimeStamp();
var oauth_signature_method = "HMAC-SHA1";
var oauth_signature = Uri.EscapeDataString(oauth.GenerateSignature(URL, key, secret, null, null, "GET", oauth_timestamp, oauth_nonce, out normalizedUrl, out normalizedqueryparameters));
String dfd = "OAuth oauth_consumer_key=\"" + key + "\", oauth_nonce=\"" + oauth_nonce + "\", oauth_signature=\"" + oauth_signature + "\", oauth_signature_method=\"" + oauth_signature_method + "\", oauth_timestamp=\"" + oauth_timestamp + "\", oauth_version=\"1.0\"";
client.DefaultRequestHeaders.Add("Authorization", dfd);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip");
using (HttpResponseMessage response = await (client.GetAsync(completeURL)))
{
if (response.IsSuccessStatusCode)
{
String content = await response.Content.ReadAsStringAsync();
}
}
}
return events;
}
I use OAuthBase.cs from https://code.google.com/p/oauth/issues/detail?id=223
When I run my project I can see in Charles that I always receive the response code "401 Unauthorized"
What am I doing wrong?
I got the OAuthBase.cs library working! My code is:
OAuthBase o = new OAuthBase();
var normalizedUrl = String.Empty;
var normalizedParameters = String.Empty;
var oauth_consumer_key = "MY KEY";
var oauth_consumer_secret = "MY SECRET";
var oauth_timestamp = o.GenerateTimeStamp();
var oauth_nonce = o.GenerateNonce();
var oauth_signature_method = "HMAC-SHA1";
var oauth_signature = o.GenerateSignature(new Uri(completeURL), oauth_consumer_key, oauth_consumer_secret, null, null, "GET", oauth_timestamp, oauth_nonce, out normalizedUrl, out normalizedParameters);
var oauth = "OAuth oauth_consumer_key=\"" + oauth_consumer_key + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) +"\",oauth_signature_method=\"" + oauth_signature_method + "\",oauth_timestamp=\"" + oauth_timestamp + "\",oauth_version=\"1.0\"";
var basestring = o.GenerateSignatureBase(new Uri(completeURL), oauth_consumer_key, null, null, "GET", oauth_timestamp, oauth_nonce, oauth_signature_method, out normalizedUrl, out normalizedParameters);
Debug.WriteLine(completeURL);
Debug.WriteLine(basestring);
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", oauth);
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json");
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip");
using (HttpResponseMessage response = await (client.GetAsync(completeURL)))
{
if (response.IsSuccessStatusCode)
{
String content = await response.Content.ReadAsStringAsync();
Debug.WriteLine(content);
}
}
}
But I still got a problem on some URL's where I want to fetch data. I found this thanks to a handy website:
I've noticed that sometimes my basestring decoding is wrong and that's why it give a 401 Unauthorized error.
Good basestring:
GET&http%3A%2F%2Fwww.uitid.be%2Fuitid%2Frest%2Fsearchv2%2Fsearch&fq%3Dlanguage%253Anl%26fq%3Dtype%253Aevent%26group%3Devent%26oauth_consumer_key%3Def08c84b91c842bbf4f182d188dd4e57%26oauth_nonce%3DNjM1NDY3MTE0NzI3NTM3MDIw%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1411107473%26oauth_version%3D1.0%26q%3D%252A%253A%252A%26rows%3D15
My basestring:
GET&http%3A%2F%2Fwww.uitid.be%2Fuitid%2Frest%2Fsearchv2%2Fsearch&fq%3Dlanguage%253Anl%26fq%3Dtype%253Aevent%26group%3Devent%26oauth_consumer_key%3Def08c84b91c842bbf4f182d188dd4e57%26oauth_nonce%3DNjM1NDY3MTE0NzI3NTM3MDIw%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1411107473%26oauth_version%3D1.0%26q%3D%2A%253A%2A%26rows%3D15
I got * characters in my URL that doesn't got encoded like OAuth wants it.
For my decoding I use the method:
private static String BuildQueryString(List<StringKeyValue> parameters)
{
if (parameters == null)
{
throw new ArgumentNullException("parameters");
}
StringBuilder builder = new StringBuilder();
foreach (var pair in parameters.Where(p => !String.IsNullOrEmpty(p.Value)))
{
if (builder.Length > 0)
{
builder.Append("&");
}
builder.Append(Uri.EscapeDataString(pair.Key));
builder.Append("=");
builder.Append(Uri.EscapeDataString(pair.Value));
}
return builder.ToString();
}
I think i need to make use of the RFC3986 encoding.. but I don't know how to implement it. I found this relevant question: How to get Uri.EscapeDataString to comply with RFC 3986
but the Uri.HexEscape method doesn't work in WP8.1
Upvotes: 2
Views: 665
Reputation: 342
I found the solution for my own problem.
Find a OAuth library compatible with WP8.1:
First u need a working library that is compatible with Windows Phone 8.1. In my case I used the class OAuthBase.cs from https://code.google.com/p/oauth/issues/detail?id=223
Encode your URL & parameters so it make use of RFC3986 (OAuth makes use of that)
I had a lot of problems with OAuth (401 Unauthorized error when fetching data) because I encoded my URL with the method Uri.EscapeDataString()
but that had problem with reserved characters like * ( ) ! ' [ ] ,
So I searched for a solution for that and found this: How to get Uri.EscapeDataString to comply with RFC 3986. The only problem was that the Uri.HexEscape function doesn't work anymore in WP8.1 so I changed the method a bit.
The method can be found here:
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ",", ")", "[","]" };
internal static string EscapeUriDataStringRfc3986(string value)
{
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
switch (UriRfc3986CharsToEscape[i])
{
case "!":
escaped.Replace(UriRfc3986CharsToEscape[i], "%21");
break;
case "*":
escaped.Replace(UriRfc3986CharsToEscape[i], "%2A");
break;
case "'":
escaped.Replace(UriRfc3986CharsToEscape[i], "%27");
break;
case "(":
escaped.Replace(UriRfc3986CharsToEscape[i], "%28");
break;
case ",":
escaped.Replace(UriRfc3986CharsToEscape[i], "%2C");
break;
case ")":
escaped.Replace(UriRfc3986CharsToEscape[i], "%29");
break;
case "[":
escaped.Replace(UriRfc3986CharsToEscape[i], "%5B");
break;
case "]":
escaped.Replace(UriRfc3986CharsToEscape[i], "%5D");
break;
}
}
return escaped.ToString();
}
Build the OAuth header and sign it to the HttpClient
For building the OAuth header u can use some methods from OAuthBase.cs:
OAuthBase o = new OAuthBase();
var normalizedUrl = String.Empty;
var normalizedParameters = String.Empty;
var oauth_consumer_key = "MY KEY";
var oauth_consumer_secret = "MY SECRET";
var oauth_timestamp = o.GenerateTimeStamp();
var oauth_nonce = o.GenerateNonce();
var oauth_signature_method = "HMAC-SHA1";
var oauth_signature = o.GenerateSignature(new Uri(completeURL), oauth_consumer_key, oauth_consumer_secret, null, null, "GET", oauth_timestamp, oauth_nonce, out normalizedUrl, out normalizedParameters);
var oauth = "OAuth oauth_consumer_key=\"" + oauth_consumer_key + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) +"\",oauth_signature_method=\"" + oauth_signature_method + "\",oauth_timestamp=\"" + oauth_timestamp + "\",oauth_version=\"1.0\"";
After building the OAuth header string you only need to add it to the HttpClient:
HttpClient client = new HttpClient()
client.DefaultRequestHeaders.Add("Authorization", oauth);
The complete code:
String completeURL = EscapeUriDataStringRfc3986("http://www.myurl.be/*!(),");
OAuthBase o = new OAuthBase();
var normalizedUrl = String.Empty;
var normalizedParameters = String.Empty;
var oauth_consumer_key = "MY KEY";
var oauth_consumer_secret = "MY SECRET";
var oauth_timestamp = o.GenerateTimeStamp();
var oauth_nonce = o.GenerateNonce();
var oauth_signature_method = "HMAC-SHA1";
var oauth_signature = o.GenerateSignature(new Uri(completeURL), oauth_consumer_key, oauth_consumer_secret, null, null, "GET", oauth_timestamp, oauth_nonce, out normalizedUrl, out normalizedParameters);
var oauth = "OAuth oauth_consumer_key=\"" + oauth_consumer_key + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) +"\",oauth_signature_method=\"" + oauth_signature_method + "\",oauth_timestamp=\"" + oauth_timestamp + "\",oauth_version=\"1.0\"";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", oauth);
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json");
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip");
using (HttpResponseMessage response = await (client.GetAsync(completeURL)))
{
if (response.IsSuccessStatusCode)
{
String content = await response.Content.ReadAsStringAsync();
//DO SOMETHING WITH CONTENT
}
}
}
Upvotes: 2