I am currently working on an app using Xamarin Forms. My development environment is Mac OS, Visual Studio, and C#. The app that I am developing will be interfacing with a web service. The service is FamilySearch, which is a genealogy website.
I have been writing some code to send requests and handle responses from their servers. I wrote a request that I believed was well-formed, but I received a response indicating "Unauthorized".
I then decided to take my code run it in a .NET Core console application, so as to avoid the overhead of using Xamarin Forms. I sent the same exact request, byte for byte. When doing this, I get a successful response (status code 200).
So, I have 2 identical HTTP requests, one being sent from the iOS simulator in a Xamarin Forms app, and the other being sent from the console in a .NET Core app. They are the receiving different responses from the server. Any idea why this could be?
Here is some code so you have an idea of what I am doing. First, I set up some HttpClient objects (one that is directed at their authentication server, another at a server the handles other calls):
HttpClient _identity_host = new HttpClient();
HttpClient _platform_host = new HttpClient();
_identity_host.BaseAddress = new System.Uri("https://identint.familysearch.org");
_platform_host.BaseAddress = new System.Uri("https://api-integ.familysearch.org");
_identity_host.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
_platform_host.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
After setting up the HttpClient objects, I then call into this net function to log in to my user account. This function is successful in both the Xamarin Forms app and the .NET Core console app:
public async void AttemptLogin(string username, string password)
{
//Form the web request
Dictionary<string, string> login_content_pairs = new Dictionary<string, string>()
{
{ "password", _password },
{ "grant_type", "password" },
{ "client_id", _application_id },
{ "username", _username }
};
string login_content = this.ToQueryString(login_content_pairs, false);
StringContent content = new StringContent(login_content, Encoding.UTF8, "application/x-www-form-urlencoded");
var result = await _identity_host.PostAsync("/cis-web/oauth2/v3/token", content);
if (result.IsSuccessStatusCode)
{
var token_json = await result.Content.ReadAsStringAsync();
JObject parsed_json = JObject.Parse(token_json);
if (parsed_json.ContainsKey("access_token"))
{
_access_token = (string)parsed_json["access_token"];
}
else if (parsed_json.ContainsKey("token"))
{
_access_token = (string)parsed_json["token"];
}
string k = (string)parsed_json["token"];
//Set the authorization header on the platform host object
_platform_host.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _access_token);
_successful_login = true;
}
}
Finally, after the login attempt completes, I then use my authorization token to request some stuff from their servers:
public async void GetCurrentPerson()
{
var result = await _platform_host.GetAsync("/platform/tree/current-person");
if (result.IsSuccessStatusCode)
{
var token_json = await result.Content.ReadAsStringAsync();
JObject parsed_json = JObject.Parse(token_json);
}
}
The above GET request is the one that is returning two different responses - depending on whether I am using the iOS simulator with Xamarin Forms or using a .NET Core console app.
Here is the ToString() of the GET request from the Visual Studio debugger:
{
Method: GET,
RequestUri: 'https://api-integ.familysearch.org/platform/tree/persons/L5FY-BQW',
Version: 1.1,
Content: <null>,
Headers:
{
Accept: application/json
Authorization: Bearer MY_AUTHORIZATION_TOKEN
}
}
From the Console app, I get this response:
{
StatusCode: 200,
ReasonPhrase: 'OK',
Version: 1.1,
Content: System.Net.Http.NoWriteNoSeekStreamContent,
Headers:
{
Cache-Control: no-transform, must-revalidate, max-age=0
Date: Wed, 04 Apr 2018 01:40:13 GMT
ETag: "137412002955880000"
Server: Apache-Coyote/1.1
Vary: Accept
Vary: Accept-Language
Vary: Accept-Encoding
Vary: Expect
Vary: Accept-Encoding
Warning: 199 FamilySearch Best Practice Violation: Should specify versioned media type in Accept header, e.g. one of [ "application/x-fs-v1+xml", "application/x-fs-v1+json", "application/atom+xml", "application/x-gedcomx-atom+json", "application/x-gedcomx-v1+xml", "application/x-gedcomx-v1+json" ].
X-PROCESSING-TIME: 184
Connection: keep-alive
Allow: OPTIONS
Allow: HEAD
Allow: GET
Allow: POST
Allow: DELETE
Content-Location: /tree/persons/L5FY-BQW
Content-Type: application/json
Last-Modified: Sat, 24 Mar 2018 16:04:55 GMT
Content-Length: 6479
}
}
While the same request from the Xamarin Forms app using the iOS simulator yields the following response:
{
StatusCode: 401,
ReasonPhrase: 'Unauthorized',
Version: 1.1,
Content: System.Net.Http.StreamContent,
Headers:
{
Cache-Control: no-cache, no-store, no-transform, must-revalidate, max-age=0
Date: Wed, 04 Apr 2018 01:45:02 GMT
Link: <https://integration.famil...
}
}
The content of the 401 Unauthorized response is the following:
{
"errors" : [ {
"code" : 401,
"message" : "Unable to read tf person.",
"stacktrace" : "GET http://tf.integ.us-east-1.dev.fslocal.org/tf/person/L5FY-BQW?oneHops=none returned a response status of 401 Unauthorized: { "401" : "Unauthorized" }"
} ]
}
Any help in understanding what is going wrong would be greatly appreciated. Thank you!