NativeMessageHandler Giving "Unauthorized" result on PostAsync - xamarin

I am using ModernHTTPClient in my application to access a Secure ReST Service.
Server is configured with TLS1.2.
Whenever i try to hit the service with valid user credential, i gets the below error.
"Unauthorized"
"System.TypeInitializationException: The type initializer for
'System.Net.HttpVersion' threw an exception."
Below is the code which i used
NativeMessageHandler handler = new NativeMessageHandler(false, false);
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.EnableUntrustedCertificates();
handler.Credentials = oNwCred;
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
using (var oClient = new HttpClient(handler))
{
//Set Timeout
oClient.Timeout = new TimeSpan(0, C_TIME_OUT, 0);
oClient.DefaultRequestHeaders.Accept.Clear();
var oContent = new StringContent(szRequestJson, Encoding.UTF8, C_CONTENT_TYPE);
return await oClient.PostAsync(szUrl, oContent);
}
Server is configured with self signed certificate.
I have tried the code from Both PCL and from Droid project as well.
Thank you in advance for your help.
Regards
Vips

Related

connect to a server with a self-signed certificate

Ive been doing some research about how to consume a web api executed on my localhost throught a xamarin app. The web api works perfect, I can adding and getting the data to/from my sql server using a web browser but if I try to connect xamarin to it Ive always received authentication error (Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED). Im basically doind this. I followed this post:
http://xamarininterviewquestion.blogspot.com/2019/06/ssl-certificate-and-public-key-pinning.html
So as it definitly didnt work, Id like to try another way;
Set TLSConfig DangerousAcceptAnyServerCertificateValidator to true. Because for know Im happy if Im able to test it.
Thats cool but as Im not a pro I have no idea about implement this;
var httpHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
Ive got "DangerousAcceptAnyServerCertificateValidator" doesnt have a definition.
Thank you all in advance and sorry if Ive not been clear.
DangerousAcceptAnyServerCertificateValidator isn't applicable to Xamarin platforms, according to its documentation.
But literally you can write the same code like,
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
HttpClient client = new HttpClient(handler);
https://stackoverflow.com/a/64741829/11182
if you are using Refit then you can do this
public HttpClient PreparedClient()
{
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => { return true; };
HttpClient client = new HttpClient(handler) { BaseAddress = new Uri(EndpointConstants.BaseUrl) };
return client;
}
private T RefitApi<T>() => RestService.For<T>(PreparedClient());
and if you need to specify settings you can do this
private T RefitApiWithToken<T>() => RestService.For<T>(PreparedClient(), refitSettings);
When using Refit for Xamarin forms
public HttpClient PreparedClient()
{
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => { return true; };
HttpClient client = new HttpClient(handler) { BaseAddress = new Uri(EndpointConstants.BaseUrl) };
return client;
}
var apiResponse = RestService.For<T>(PreparedClient());

AcquireTokenSilentAsync not working

I have the following setup:
var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
string redirectUri = Url.Action("Authorize", "Planner", null, Request.Url.Scheme);
Uri authUri = authContext.GetAuthorizationRequestURL("https://graph.microsoft.com/", SettingsHelper.ClientId,
new Uri(redirectUri), UserIdentifier.AnyUser, null);
// Redirect the browser to the Azure signin page
return Redirect(authUri.ToString());
This takes you to:
// Get the 'code' parameter from the Azure redirect
string authCode = Request.Params["code"];
// The same url we specified in the auth code request
string redirectUri = Url.Action("Authorize", "Planner", null, Request.Url.Scheme);
// Use client ID and secret to establish app identity
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
//FileTokenCache at specific location
TokenCache fileTokenCache = new FilesBasedAdalV3TokenCache("C:\\temp\\justin.bin");
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthorityTenantID, fileTokenCache);
AuthenticationResult authResult = null;
try
{
// Get the token silently first
authResult = await authContext.AcquireTokenSilentAsync(SettingsHelper.O365UnifiedResource, credential, UserIdentifier.AnyUser);
}
catch (AdalException ex)
{
authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, fileTokenCache);
authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(authCode, new Uri(redirectUri), credential, SettingsHelper.O365UnifiedResource);
}
The token is successfully saved in the file and it seems that it is also being successfully retrieved. However the silent token acquisition still gives an exception to get token first using the non silent function. What am I missing please?
Note that O365UnifiedResource is set to https://graph.microsoft.com/
solved this by using
new UserIdentifier("<email address used to login microsoft apps>", UserIdentifierType.RequiredDisplayableId)
instead of
UserIdentifier.AnyUser
and fixed the client ID to be the APP ID as specified in the registration of the app

Drupal 7 Service module node resources attach_file end point

I am implementing a Xamarin Form mobile app to allow post photo to Drupal using service module node resources. http://xxxx.com/{endpoint}/node/4/attach_file
i able to post from POSTMAN with
I tried to implement with c# HttpClient but keep getting response like "401 :Missing required argument field_name"
Please help on my code:
var httpClient = new HttpClient(new NativeMessageHandler());
httpClient.Timeout.Add(new TimeSpan(0, 0, 30));
httpClient.BaseAddress = new Uri(BaseAddress);
var content = new MultipartFormDataContent();
var streamContent = new StreamContent(g_media.GetStream());
streamContent.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse("form-data");
streamContent.Headers.ContentDisposition.Parameters.Add(new NameValueHeaderValue("field_name", "field_receipt_image"));
content.Add(streamContent,"files[file]");
var response = await httpClient.PostAsync("node/4/attach_file", content);
response.EnsureSuccessStatusCode();
I had the same issue and used RestSharp to resolve it. Here is the code I used to upload a file to Drupal:
var restClient = new RestClient("http:XXXXXX/attach_file");
var request = new RestRequest(Method.POST);
request.AddFile("files[file]", fileName);
request.AddParameter("field_name", field);
IRestResponse response = restClient.Execute(request);

WebRequest returns 404 when switching to SSL

Having built an app using PCL method in Xamarin and have had it working 100% using standard HTTP I now changed the remote test server to use SSL with self signed certs.
The app contacts a custom API for logging onto a server and querying for specific data.
I've changed the app to look at SSL now and initially got an error regarding Authentication not working or something but turned off SSL related errors for testing using:
ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
in my AppDelegate files FinishedLaunching method which got over that error.
I'm now getting a 404 / protocol error when trying to do my Login POST to the given URL.
I am using HttpWebRequest for my RESTful calls and this works fine if I change back to plain http.
Not sure why but some articles suggested using ModernHttpClient, which I did. I imported the component (also added the package using NuGet) to no avail.
Am I missing something else that I should be configuring in my code related to httpwebresponse when contacting the SSL server or is this component simply incapable of speaking to an SSL server?
My login function is as follows (Unrelated code removed/obfuscated):
public JsonUser postLogin(string csrfToken, string partnerId, string username, string password){
string userEndPoint = SingletonAppSettngs.Instance ().apiEndPoint;
userEndPoint = userEndPoint.Replace ("druid/", "");
var request = WebRequest.CreateHttp(string.Format(this.apiBaseUrl + userEndPoint + #"user/login.json"));
// Request header collection set up
request.ContentType = "application/json";
request.Headers.Add ("X-CSRF-Token", csrfToken);
// Add other configs
request.Method = "POST";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json_body_content = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}";
streamWriter.Write(json_body_content);
streamWriter.Flush();
streamWriter.Close();
}
try{
HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse();
using (StreamReader reader = new StreamReader (httpResponse.GetResponseStream ())) {
var content = reader.ReadToEnd ();
content = content.Replace ("[],", "null,");
content = content.Replace ("[]", "null");
if (content == null) {
throw new Exception ("request_post_login - content is NULL");
} else {
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.NullValueHandling = NullValueHandling.Ignore;
JsonUser deserializedUser = JsonConvert.DeserializeObject<JsonUser>(content, jss);
if(content.Contains ("Hire company admin user")){
deserializedUser.user.roles.__invalid_name__5 = "Hire company admin user";
deserializedUser.user.roles.__invalid_name__2 = "authenticated user";
}
return deserializedUser;
}
}
}catch(Exception httpEx){
Console.WriteLine ("httpEx Exception: " + httpEx.Message);
Console.WriteLine ("httpEx Inner Exception: " + httpEx.InnerException.Message);
JsonUser JsonUserError = new JsonUser ();
JsonUserError.ErrorMessage = "Error occured: " + httpEx.Message;
return JsonUserError;
}
}
When making a Web Request using ModernHttpClient, I generally follow the pattern below. Another great library created by Paul Betts is refit, and can be used to simplify rest calls.
using (var client = new HttpClient(new NativeMessageHandler(false, false)))
{
client.BaseAddress = new Uri(BaseUrl, UriKind.Absolute);
var result = await Refit.RestService.For<IRestApi>(client).GetData();
}
The second parameter for NativeMessageHandler should be set to true if using a customSSLVerification.
Here's a look at IRestApi
public interface IRestApi
{
[Get("/foo/bar")]
Task<Result> GetMovies();
}
Number of things I had to do to get this to work.
The Self Signed Cert had to allow TLS 1.2
As the API is Drupal based, HTTPS had to be enabled on the server and a module installed to manage the HTTP specific pages.

(401) Unauthorized Error When Calling Web API from a Console Application

When I call my WEB API from my Console Application, I encounter:
The remote server returned an error: (401) Unauthorized.
This application runs in Interanet (Windows Authentication)
Uri uri = new Uri("http://myServer/api/main/foo");
WebClient client = new WebClient();
client.Credentials = CredentialCache.DefaultCredentials;
using (Stream data = client.OpenRead(uri))
{
using (StreamReader sr = new StreamReader(data))
{
string result = sr.ReadToEnd();
Console.WriteLine(result);
}
}
Updated
If I replace
client.Credentials = CredentialCache.DefaultCredentials;
with this line
client.Credentials = new NetworkCredential( username, password);
it works fine but I need the current credential to be set automatically.
Any idea?
Thanks in advance ;)
You use the default windows credentials here
client.Credentials = CredentialCache.DefaultCredentials;
Specify the credential that you want to authenticate using the following code:
var credential = new NetworkCredential(, , );
serverReport.ReportServerCredentials.NetworkCredentials = credential;
following line is the cause of this behaviour :
client.Credentials = CredentialCache.DefaultCredentials;
Actually this line assigns the credentials of the logged in user or the user being impersonated ( which is only possible in web applications ) , so what I believe is that you have to provide credentials explicitly (http://msdn.microsoft.com/en-us/library/system.net.credentialcache(v=vs.110).aspx) , thanks.

Resources