Xamarin Essentials WebAuthenticator.AuthenticateAsync method doesn't return any result - xamarin.forms

I have added a WebAuthenticator.AuthenticateAsync method in my xamarin forms app with start up Url as "https://accounts.google.com/o/oauth2/auth"
and call back url as "myapp://"
I have also tried with call back url as "com.googleusercontent.apps.{clientId}:/oauth2redirect"
I am doing this to add google login in my xamarin forms app.
On this browser with available google accounts are been showing up and after successful completion of email authentication it returns to app but result is not returned from WebAuthenticator.AuthenticateAsync method.
On second time invocation of this method returns the first invocation result as cancelled by user and the browser opens again for second time email authentication.
But it works in ios.
I have added 3 classes
public class Auth0Client
{
private readonly OidcClient oidcClient;
public Auth0Client(Auth0ClientOptions options)
{
var discovery = new DiscoveryPolicy
{
ValidateEndpoints = false,
Authority = "https://accounts.google.com"
};
oidcClient = new OidcClient(new OidcClientOptions
{
Authority = $"https://accounts.google.com/o/oauth2/auth",
ClientId = options.ClientId,
Scope = options.Scope,
RedirectUri = options.RedirectUri,
Browser = options.Browser,
ProviderInformation = options.ProviderInformation,
Policy = new Policy
{
Discovery = discovery,
RequireAccessTokenHash = false
},
});
}
public IdentityModel.OidcClient.Browser.IBrowser Browser
{
get
{
return oidcClient.Options.Browser;
}
set
{
oidcClient.Options.Browser = value;
}
}
public async Task<LoginResult> LoginAsync()
{
return await oidcClient.LoginAsync();
}
}
public class Auth0ClientOptions
{
public Auth0ClientOptions()
{
}
public string Domain { get; set; }
public string ClientId { get; set; }
public string RedirectUri { get; set; }
public string Scope { get; set; }
public IBrowser Browser { get; set; }
public ProviderInformation ProviderInformation { get; set; }
}
public class WebBrowserAuthenticator : IBrowser
{
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
try
{
WebAuthenticatorResult result = await WebAuthenticator.AuthenticateAsync(
new Uri(options.StartUrl),new Uri(options.EndUrl));
var url = new RequestUrl(options.EndUrl)
.Create(new Parameters(result.Properties));
return new BrowserResult
{
Response = url,
ResultType = BrowserResultType.Success
};
}
catch (Exception ex)
{
return new BrowserResult
{
ResultType = BrowserResultType.UserCancel,
ErrorDescription = "Login canceled by the user."
};
}
}
}
In view model we are creating authoclient
private async void NavigateToGoogleLogin(object obj)
{
string clientId = null;
string redirectUri = null;
switch (Device.RuntimePlatform)
{
case Device.iOS:
clientId = AppConstants.GoogleiOSClientId;
redirectUri = AppConstants.GoogleiOSRedirectUrl;
break;
case Device.Android:
clientId = AppConstants.GoogleAndroidClientId;
redirectUri = AppConstants.GoogleAndroidRedirectUrl;
break;
}
var auth0client = new Auth0Client(new Auth0ClientOptions()
{
Domain = "accounts.google.com/o/oauth2/auth",
ClientId = clientId,
RedirectUri = redirectUri,
Scope = AppConstants.GoogleScope,
Browser = new WebBrowserAuthenticator(),
ProviderInformation = new ProviderInformation
{
IssuerName = "accounts.google.com",
AuthorizeEndpoint = AppConstants.GoogleAuthorizeUrl,
TokenEndpoint = AppConstants.GoogleAccessTokenUrl,
UserInfoEndpoint = AppConstants.GoogleUserInfoUrl,
KeySet = new JsonWebKeySet(),
},
});
var loginResult = await auth0client.LoginAsync();
}
We are using below constants in authoclient object creation
internal static string GoogleScope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
internal static string GoogleAuthorizeUrl = "https://accounts.google.com/o/oauth2/auth";
internal static string GoogleAccessTokenUrl = "https://www.googleapis.com/oauth2/v4/token";
internal static string GoogleUserInfoUrl = "https://www.googleapis.com/oauth2/v3/userinfo";
Xamarin forms version:5.0.0.2012
Xamarin essentials: 1.7.3
Thanks in advance

Related

Consuming web api with RestSharp basic authentication return null

I am trying to consume an endpoint with RestSharp with Basic authentication.
I followed the instructions on the documentation https://restsharp.dev/getting-started/getting-started.html
The request was successful but I think the request body was malformed.
How can I get this to work
internal BalanceInquiryResponse BalanceInquiryRest(BalanceInquiryRequest BalanceInquiryRequest, Settings Settings)
{
// BalanceInquiryResponse BalanceInquiryResponse = new BalanceInquiryResponse();
var client = new RestClient(Settings.BaseUrl + "All/Inquiry");
client.Authenticator = new HttpBasicAuthenticator(Settings.Username, Settings.Password);
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddJsonBody(new
{
Acc = BalanceInquiryRequest.Acc
});
IRestResponse response = client.Execute(request);
IRestResponse<BalanceInquiryResponse> res = client.Execute<BalanceInquiryResponse>(request);
if (response.IsSuccessful)
{
BalanceInquiryResponse = new BalanceInquiryResponse
{
responseInquiry = res.Data.responseInquiry,
ResponseDescription = res.Data.ResponseDescription,
ResponseMessage = res.Data.ResponseMessage
};
return BalanceInquiryResponse;
}
else
{
BalanceInquiryResponse = new BalanceInquiryResponse
{
ResponseDescription = responseses.ErrorMessage,
};
return BalanceInquiryResponse;
}
}
This is my response body
{
"responseMessage": "Successful",
"responseDescription": "Request Successful",
"responseInquiry": null
}
When I tried with postman I got
{
"ResponseMessage": "Successful",
"ResponseDescription": "Request Successful",
"response": {
"AvalBal": 586324.42,
"ReverAmt": 0,
"AccCurrency": "US "
}
}
IRestResponse<BalanceInquiryResponse> res = client.Execute<BalanceInquiryResponse>(request);
So there is a specific reason...you are putting BalanceInquiryResponse in the generic IRestResponse above.
With the above call, this should automatically hydrate the BalanceInquiryResponse object, and you shouldn't need to hand map.
Aka, you should ~not~ need this below code:
BalanceInquiryResponse = new BalanceInquiryResponse
{
responseInquiry = res.Data.responseInquiry,
ResponseDescription = res.Data.ResponseDescription,
ResponseMessage = res.Data.ResponseMessage
};
I think your issue is that your POCO object (BalanceInquiryResponse) should perfectly match the "structure" of the JSON.
Change your BalanceInquiryResponse to PERFECTLY match the json "properties".
and recognize you have a nested object.
I think it it would be:
public class ResponsePoco {
public double AvalBal { get; set; }
public int ReverAmt { get; set; }
public string AccCurrency { get; set; }
}
public class BalanceInquiryResponse{
public string ResponseMessage { get; set; }
public string ResponseDescription { get; set; }
public ResponsePoco response { get; set; }
}
Pay attention the to "ResponsePoco response"..note the variable name is LOWERCASE .. because...the json has a lowercase "response" in it.
I have called the (child) object "ResponsePoco" to highlight the difference between the object name and the variable name.
If you cannot "perfectly" match the Poco properties. you can use attributes to "massage" the discrepencies. As seen here:
https://www.newtonsoft.com/json/help/html/JsonPropertyName.htm
public class Videogame
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("release_date")]
public DateTime ReleaseDate { get; set; }
}

how to generate a Http Request for AAD?

so, I get the Access Token and I want to create a request...
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", accesToken);
HttpResponseMessage response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/users");
Also, the Problem may be on setting the scopes for the authentication...i set it to:
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
But I don't know if it gets all application permissions that I have (it is a deamon).
You can use this sample which will help you to create events with same client credential flow which you are using but you need to change some things here.
You need to first give the Calendar.ReadWrite permission in the Azure portal for your app.
You need to add the below code in the Program.cs
if (result != null)
{
var httpClient = new HttpClient();
var apiCaller = new ProtectedApiCallHelper(httpClient);
await apiCaller.CallWebAPIToPostEvent($"{config.ApiUrl}v1.0/users/{user obj id}/calendars/{calendar id}/events", result.AccessToken, Display);
}
Then you need to add the below classes in the protectedApiCallHelper.cs
public class Event
{
[JsonProperty("subject")]
public string Subject { get; set; }
[JsonProperty("body")]
public Body Body;
[JsonProperty("start")]
public TimeAndDate Start;
[JsonProperty("end")]
public TimeAndDate End;
[JsonProperty("location")]
public Location Location;
[JsonProperty("attendees")]
public List<Attendees> Attendees;
}
public class Body
{
[JsonProperty("contentType")]
public string ContentType { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
public class TimeAndDate
{
[JsonProperty("dateTime")]
public string DateTime { get; set; }
[JsonProperty("timeZone")]
public string TimeZone { get; set; }
}
public class Location
{
[JsonProperty("displayName")]
public string DisplayName { get; set; }
}
public class Attendees
{
[JsonProperty("emailAddress")]
public EmailAddress EmailAddress;
[JsonProperty("type")]
public string Type;
}
public class EmailAddress
{
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
In this same ProtectedApiCallHelper class you can create a post request and get the details by adding the below code
public async Task CallWebAPIToPostEvent(string webApiUrl, string accessToken, Action<JObject> processResult)
{
var defaultRequetHeaders = HttpClient.DefaultRequestHeaders;
if (defaultRequetHeaders.Accept == null || !defaultRequetHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequetHeaders.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
var payload = new Event
{
Subject = "Let's go for lunch",
Body = new Body
{
ContentType = "HTML",
Content = "Does mid month work for you?"
},
Start = new TimeAndDate
{
DateTime = "2019-03-15T12:00:00",
TimeZone = "Pacific Standard Time"
},
End = new TimeAndDate
{
DateTime = "2019-03-15T14:00:00",
TimeZone = "Pacific Standard Time"
},
Location = new Location
{
DisplayName = "Harry's Bar"
},
Attendees = new List<Attendees>
{
new Attendees
{
EmailAddress = new EmailAddress
{
Address = "Shiva#nishantsingh.live",
Name = "Shiva"
},
Type = "required"
}
}
};
// Serialize our concrete class into a JSON String
var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(payload));
// Wrap our JSON inside a StringContent which then can be used by the HttpClient class
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
HttpResponseMessage response = await HttpClient.PostAsync(webApiUrl, httpContent);
if (response.Content != null)
{
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
}
This will help you create the event with these details.

Error retrieving data from Api Controller

I'm working on an ASP.NET Core Api and Xamarin forms client using Visual Studio 2017.
I'm getting an error
System.Runtime.Serialization.SerializationException: Invalid JSON string
because response.Content is null, when retrieving data from API but when paste this Url in browser "https://localhost:44305/api/Agreement/GetAgreementText/1" it shows data in the browser. When I run using client it's not hit to api method debug point .
Here is my APi method
[HttpGet]
[Route("GetAgreementText/{id}")]
public DefaultApiResult GetAgreementText(long Id)
{
Company com = _companyRepository.Get(Id);
string st = com.AgreementText;
DefaultApiResult result = new DefaultApiResult
{
Data = st
};
return result;
}
Here is my client application Api invoking method
public string GetAgreementTextLoading(long idCompany)
{
string agreementText = "";
// var token = _tokenService.GetLastActivateToken().Hash;
var clientURL = "https://localhost:44305/";
var client = new RestClient(clientURL);
var request = new RestRequest("api/Agreement/GetAgreementText/{Id}", Method.GET);
request.AddUrlSegment("Id", idCompany.ToString());
IRestResponse response = client.Execute(request);
AppRestResponse apiResponse = SimpleJson.DeserializeObject<AppRestResponse>(response.Content);
var statusMessage = "";
if (apiResponse.Success)
{
statusMessage = "Success.";
if (!string.IsNullOrEmpty(response.Content))
{
agreementText = apiResponse.Data.ToString();
}
else
{
throw new Exception("Invalid response");
}
}
else
{
agreementText = "Error retrieving agreement text";
}
return agreementText;
}
public class AppRestResponse
{
public bool Success { get; set; }
public object Data { get; set; }
public IEnumerable<AppRestReponseError> ErrorMessages { get; set; }
}
public class DefaultApiResult
{
public bool Success
{
get
{
return ErrorMessages.Count == 0;
}
private set { }
}
public List<ErrorMessage> ErrorMessages { get; set; }
public object Data { get; set; }
public DefaultApiResult()
{
ErrorMessages = new List<ErrorMessage>();
}
public DefaultApiResult(string errorMessage)
:this()
{
ErrorMessages.Add(new ErrorMessage()
{
Message = errorMessage
});
}
public DefaultApiResult(string[] errorMessages)
:this()
{
foreach (var errorMessage in errorMessages)
{
ErrorMessages.Add(new ErrorMessage()
{
Message = errorMessage
});
}
}
}
I'm not sure about the SimpleJson and the rest client you are using .
However , assuming you're using the RestSharp , it seems that there's no need to use the SimpleJson to deserialize response here .
I just remove the following codes :
IRestResponse response = client.Execute(request);
AppRestResponse apiResponse = SimpleJson.DeserializeObject<AppRestResponse>(response.Content);
and add the following two lines:
IRestResponse<AppRestResponse> response = client.Execute<AppRestResponse>(request);
var apiResponse= response.Data;
It works as expected .

Unable to update domain details using Godaddy API in ASP.Net

I'm trying to update nameserver for a domain that is currently registered using OTE Environment. I am able to register the domain but unable to update the nameserver for the registered domain. The error i receive is
"INVALID_BODY\",\"fields\":[{\"code\":\"UNEXPECTED_TYPE\",\"message\":\"is not a array\",\"path\":\"records\"}"
Below is my code.
GoDaddyDomain.cs
DomainUpdate sub = new DomainUpdate();
sub.locked = true;
sub.nameServers = new List<string>();
sub.nameServers.Add("ns1.tsohost.co.uk");
sub.nameServers.Add("ns2.tsohost.co.uk");
sub.renewAuto = true;
sub.subaccountId = "196209292";
string _secret = "xxxx-xxxx-xxxx";
string _apikey = "xxxx-xxxx-xxxx-xxxx-xxxx";
string MessageType = "application/json";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("sso-key", _apikey + ":" + _secret);
using (var requests = new HttpRequestMessage(System.Net.Http.HttpMethod.Put, String.Format("https://api.ote-godaddy.com/v1/domains/MyTestDomain000001.com/records/")))
{
requests.Headers.Add("Accept", MessageType);
var response = new HttpResponseMessage();
var json = Newtonsoft.Json.JsonConvert.SerializeObject(sub);
requests.Content = new StringContent(value);
requests.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(MessageType);
using (var responses = await client.SendAsync(requests).ConfigureAwait(false))
{
string responseXmls = await responses.Content.ReadAsStringAsync().ConfigureAwait(false);
var codes = response.StatusCode;
}
requests.Content.Dispose();
}
}
}
catch (Exception ex) { }
DomainUpdate.cs
public class DomainUpdate
{
public bool locked { get; set; }
public List<string> nameServers { get; set; }
public bool renewAuto { get; set; }
public string subaccountId { get; set; }
}

How to consume this web service?

This is my first time creating a web service. I am not sure if my implementation is incorrect, but I am trying to use much like a class. The problem is that when I am trying to consume I am getting confused and not being able to set the values of the properties.
here is the web service.
public class Service1 : System.Web.Services.WebService
{
private bool _isUserActive { get; set; }
private bool _isCredentialValid { get; set; }
public string email { get; set; }
public string pass { get; set; }
public int customerID { get; set; }
[WebMethod]
public bool VerifyUserCredential()
{
bool result = false;
PURLDataContext purl = new PURLDataContext();
try
{
var res = purl.Sel_User(email.ToLower(), pass);
if (res != null)
result = true;
_isUserActive = true;
_isCredentialValid = true;
}
catch (Exception ex)
{
if (ex.Message == "Account is inactive, please contact your administrator!")
{
_isUserActive = false;
_isCredentialValid = false;
}
else
_isCredentialValid = false;
//Invalid credentials.
}
return result;
}
[WebMethod]
public ArrayList retrieveCustomerInfo()
{
ArrayList customerInfo = new ArrayList();
string validate = "Please Validate";
if (_isCredentialValid)
{
PURLDataContext purl = new PURLDataContext();
var customer = purl.Sel_Recipient(customerID);
foreach (var c in customer)
{
customerInfo.Add(c);
}
}
else
customerInfo.Add(validate);
return customerInfo;
}
}
Here is what I am trying to do to consume.
PURLServices.Service1SoapClient webserv = new Service1SoapClient();
bool result;
ArrayOfAnyType array = new ArrayOfAnyType();
webserv.email = "email#email.com";
webserv.pass = "pass";
webserv.customerID = 12345;
result = webserv.VerifyUserCredential();
array = webserv.retrieveCustomerInfo();
Thank you for any help/
You do not want to try to use properties like this. Your method should look more like this:
public bool VerifyUserCredential(string userName, string password)
{
// method body here
}
Probably you would want to return an access token of some sort that the server will cache. This can then be passed into other methods to show that the user is valid.

Resources