DisplayAlert not working in a function in xamarin - xamarin.forms

I am making a get request to api and it is giving me correct results! Parameters are passed to it and they are used in get request !
If parameters I passed are correct then "Authentication is Sucessfull is printed after get call !" However if Parameters are not correctly passed Application is crashing !
Invalid Credentials Alert is not getting printed ! What I am doing wrong ?
async void Call(parameters)
{
string Url="xxx Api Url?parameters";
HttpClient client = new HttpClient();
string response = await client.GetStringAsync(Url);
if(response.Equals("200"))
{
await DisplayAlert("Alert", "Authentication is Successful", "Ok");
}
else
{
await DisplayAlert("Alert", "Invalid Credentials Added", "Ok");
}
}

Since this is happening inside an async method your Display Alert must need to be called on UI thread.
Device.BeginInvokeOnMainThread(async()=>{
await DisplayAlert("Alert", "Authentication is Successful", "Ok");
});
Also, your method seems to be an async void, it is generally advised as a bad practice to have an async void unless it is a lifecycle method or an event, You should consider using Threading Tasks instead i.e. async Task Call(parameters).
A Microsoft blog for best practices with async-await can be found here: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

string response = await client.GetStringAsync(Url); is most likely raising an exception when parameters is incorrect.
An uncaught exception will always crash your application.
For instance,
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("https://jsonplaceholder.typicode.com/posts/1"); // works perfectly fine.
var response2 = await client.GetStringAsync("https://jsonplaceholder.typicode.com/posts/1fdqsfdqsfdqs"); //Throws a HttpRequestException because it is not found
In your case,
await DisplayAlert("Alert", "Invalid Credentials Added", "Ok");
Should be inside the block of a try {..} catch(xx) statement

Related

Core3/React confirmation email not sent

This question applies to a core3/react project with an external identity provider, created as follows.
dotnet new react --auth Individual --use-local-db --output conf
and modified to support an external identity provider. The package is added
dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount
and startup is modified
services.AddAuthentication()
.AddMicrosoftAccount(options =>
{
options.ClientId = Configuration["Authentication:Microsoft:ClientId"];
options.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
options.CallbackPath = "/signin-microsoft";
})
After following the instructions provided by Microsoft I tested my work by registering as a user. No errors were thrown but the promised confirmation email never arrived.
Following the troubleshooting advice at the end of the instructions I set a breakpoint at the start of the SendEmailAsync method of my implementation of IEmailSender and repeated the exercise. The breakpoint is not hit.
If I manually confirm the account by updating the database,
I am able to log in.
The Forgot Password link takes me to a password recovery page and using this hits my breakpoint and successfully sends a password reset email with a link that works.
Clearly my implementation of IEmailSender works and is correctly registered. It's not exactly the same as the sample code because I have my own Exchange server and didn't use SendGrid, but it sent an email successfully for password reset and I can repeat this any number of times without a hitch.
Against the slim possibility that it is somehow the cause of the problem, here's my implementation
public class SmtpEmailSender : IEmailSender
{
public SmtpEmailSender(IOptions<SmtpOptions> options)
{
this.smtpOptions = options.Value;
}
private SmtpOptions smtpOptions { get; }
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
var smtp = new SmtpClient();
if (!smtpOptions.ValidateCertificate)
{
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
}
smtp.Connect(smtpOptions.Host, smtpOptions.Port, SecureSocketOptions.Auto);
if (smtpOptions.Authenticate)
{
smtp.Authenticate(smtpOptions.Username, smtpOptions.Password);
}
var message = new MimeMessage()
{
Subject = subject,
Body = new BodyBuilder() { HtmlBody = htmlMessage }.ToMessageBody()
};
message.From.Add(new MailboxAddress(smtpOptions.Sender));
message.To.Add(new MailboxAddress(email));
return smtp.SendAsync(FormatOptions.Default, message).ContinueWith(antecedent =>
{
smtp.Disconnect(true);
smtp.Dispose();
});
}
}
Registration in startup.cs looks like this.
services.AddTransient<IEmailSender, SmtpEmailSender>();
services.Configure<SmtpOptions>(Configuration.GetSection("SmtpOptions"));
SmptOptions is just settings hauled out of appsettings.json and injected into the ctor. Obviously that aspect works or password reset emails wouldn't work.
There can't be anything wrong with the registration because the app stops producing a message about needing to read and follow the account confirmation instructions I linked.
To see whether the problem was caused by some inadvertent side-effect of my code I created an instrumented stub of IEmailSender
public class DummyEmailSender : IEmailSender
{
private readonly ILogger logger;
public DummyEmailSender(ILogger<DummyEmailSender> logger)
{
this.logger = logger;
}
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
logger.LogInformation($"SEND EMAIL\r\nemail={email} \r\nsubject={subject}\r\nhtmlMessage={htmlMessage}\r\n{new StackTrace().ToString().Substring(0,500)}");
return Task.CompletedTask;
}
}
I also updated service registration to match.
This is the simplest possible instrumented stub, and the observed behaviour is the same, it's invoked when the Forgot Password form is submitted and is not invoked when the Confirm Registration form is submitted.
Has anyone ever got the horrible thing to work? How?
Immediately before the failure, this URL https://wone.pdconsec.net/Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Callback looks like this
Inspecting the page we find the Register button posts a form to /Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Confirmation
The code for this is available from the dotnet repository.
After cloning the repo https://github.com/dotnet/aspnetcore.git I read the build instructions and succeeded in building dotnet 5 preview. Then I ran clean before switching to the tagged branch release/3.1 to build debugging packages for core3.1 but this fails because the tagged branch brings into play a version of msbuild that's just slightly too old and the remedy suggested by the error message doesn't seem to work. Since my grip on PowerShell is weak (the build script is PowerShell) I am reduced to code inspection. The pertinent code looks like this.
public override async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
// If account confirmation is required, we need to show the link if we don't have a real email sender
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
}
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
ProviderDisplayName = info.ProviderDisplayName;
ReturnUrl = returnUrl;
return Page();
}
It looks like it ought to work. What do we know?
No unhandled errors are thrown, it makes it through to RegisterConfirmation which puts up a message about the email that never comes.
CreateUser is invoked and succeeds. We know this because the user is created in the database. So it definitely gets past there, which implies that ModelState isn't null and .IsValid is true.
IEmailSender.SendEmailAsync is not actually invoked, despite the code above.
If result.Succeeded is true there should be a log message saying something like "User created an account using Microsoft Account provider"
It redirects to https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname#outlook.com
I'm seeing log messages for most things. Trying to register a second time after the first pass creates the user but fails to send the email, a warning about a DuplicateUserName appears on the console and in the event log. Setting the confirmation directly in the database we are able to log in and then interactively delete the account, and logs appear for these activities.
But no logs appear for confirmation. What's really doing my head in is the fact that it then redirects to https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname#outlook.com
That's crazy. In order to get to there, userManager.AddLoginAsync() must return true and the very next line in that case is a write to the logger about creating the user account.
This makes no sense.
You should send confirmation email yourself, it doesn't send automatically.
After registering your user:
string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
string urltoken = Base64UrlEncoder.Encode(token);
string link = string.Format(emailOptions.ConfirmationUrl, user.Id, urltoken);
string body = $"<a href='{link}'>confirm</a>";
await emailSender.SendEmailAsync(user.Email, "confirmation", body);
I created a whole new project and worked the exercise. It works perfectly.
What's the difference? The failing version was added to an existing project that has been jerked back and forth between 3.0 and 3.1 several times in the course of troubleshooting CICD issues. Clearly it's damaged in some unobvious way and this is a non-issue.
The only reason I haven't deleted the whole question is others may fall down this hole.

Xamarin.Forms get new Token when session is over

I have this scenario: Xamarin.Forms App connected with Web Api 2. I make all requests and get the data i want. Now when the session token expires, i need to refresh the token but don't logout the user. The user don't need to know when token is refreshed. How to organize this, add in every request if statement when i send it and check if token expires.
This is one of my requests:
public async Task<User> GetProfileSetup()
{
try
{
if (CrossConnectivity.Current.IsConnected)
{
string token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(#"api/Profile/GetProfilSetup");
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
User user = JsonConvert.DeserializeObject<User>(jsonMessage);
return user;
}
else
{
var m = response.Content.ToString();
return null;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
string error = ex.Message;
return null;
}
}
P.S I have Methods for GetToken and RefreshToken in my Api and they are working, just how to organize Refreshing ?
It really depends on what libraries are you using on your project.
But let's say you're using plain c# to handled your HTTP calls.
[OPTION 1] Polly
I can recommend you looking at Polly
It's a great library with a lot of features. You can use the Retry policy to handled expired tokens:
var _unauthorizedPolicy = Policy
.Handle<Exception>(ex => ex.StatusCode == HttpStatusCode.Unauthorized) // check here for your exception to be the right one
.RetryAsync(3, async (exception, retryCount, context) =>
{
try
{
var token = await _authService.RefreshToken();
// save the new token or whatever you need to store it
}
catch (Exception ex)
{
// RefreshToken failed, you should probably sign out the user
SignOut();
}
});
What this does is that Polly will try to execute your normal HTTP call and in case it fails and the cause is specified in Handle, then a retry mechanism is fired that will try to refresh the token and then retry your request. In the end, in case the token cannot be refreshed, you sign out the user. Of course, all this can be customized, check Polly's documentation is pretty well written.
Please note that inside Handle<T> you must put the right exception. I just used Exception as a placeholder since I'm not sure what Exception is thrown in your case.
Then you would call your method with this policy:
var result = await _unauthorizedPolicy.ExecuteAsync(() => GetProfileSetup())
And you can reuse that policy for any call, no need to create it every time.
[OPTION 2] DelegatingHandler
I will like here another StackOverflow answer:
How to Refresh a token using IHttpClientFactory
Basically you can intercept every HTTP call made via a HttpClient and refresh/add a token to your requests.
Note that that answer does not obligate you to use IHttpClientFactory, it also works for a simple HttpClient.
Also a little bit off-topic. You might want to look up for libraries to handle htt calls such as Retrofit. It will really reduce the amount of boilerplate code.

GetAsync and PostAsJsonAsync methods passing parameters JsonReaderException error

I am trying to build a client side of making requests. So i do not know how i pass parameters on GetAsync method. The same problem on PostAsJsonAsync method.
Here is my code:
public static async Task<List<Users>> GetUsers(HttpClient client, Users users, string accessToken)
{
try
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.GetAsync("/api/2.0/users/?id=5&name=name");
response.EnsureSuccessStatusCode();
List<Users> listUsers= await response.Content.ReadAsAsync<List<Users>>();
Console.WriteLine("Returned list.");
return listUsers;
}
catch (HttpRequestException e)
{
Console.WriteLine("{0}", e.Message);
Console.ReadLine();
throw;
}
}
When i make this request from postman i get the results that i want. The Users class has more variables than the 2 that i request.
GetAsync without parameters works fine. When i run the project i get the error "JsonReaderException: Input string '100.0' is not a valid integer"
Is there another option to pass arguments on url?
I changed the int type of that integer property to float and the problem solved.

Office 365 Rest Api Having issues getting access token

So far i have this.
public static async Task<OutlookServicesClient> CreateOutlookClientAsync(string capability)
{
try
{
string authority = CommonAuthority;
// Create an AuthenticationContext using this authority.
_authenticationContext = new AuthenticationContext(authority);
//See the Discovery Service Sample (https://github.com/OfficeDev/Office365-Discovery-Service-Sample)
//for an approach that improves performance by storing the discovery service information in a cache.
DiscoveryClient discoveryClient = new DiscoveryClient(
async () => await GetTokenHelperAsync(_authenticationContext, DiscoveryResourceId));
// Get the specified capability ("Contacts").
CapabilityDiscoveryResult result =
await discoveryClient.DiscoverCapabilityAsync(capability);
var client = new OutlookServicesClient(
result.ServiceEndpointUri,
async () =>
await GetTokenHelperAsync(_authenticationContext, result.ServiceResourceId));
return client;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
if (_authenticationContext != null && _authenticationContext.TokenCache != null)
_authenticationContext.TokenCache.Clear();
return null;
}
}
}
private static async Task<string> GetTokenHelperAsync(AuthenticationContext context, string resourceId)
{
string accessToken = null;
AuthenticationResult result = null;
string myId = WebConfigurationManager.AppSettings["ida:ClientID"];
string myKey = WebConfigurationManager.AppSettings["ida:Password"];
ClientCredential client = new ClientCredential(myId,myKey);
result = await context.AcquireTokenAsync(resourceId, client);
//result =context.AcquireToken(resourceId, ClientID,_returnUri);
accessToken = result.AccessToken;
return accessToken;
}
When i get to result one of two things happen if i user AcquireTokenAsync i get an error stating Application with identifier XXXX was not found in directory api.office.com otherwise if i run AcquireToken i get the login modal to pop but an error occurs indicating the request must contain client_secret .
I have no idea how to resolve this issue i suspect it may have something to do with the actual app configuration i have tried both creating my own app in Azure AD and using VS Connected Service, Has Anyone Else ran into a similar issues?
Based on the errors you're seeing, there seems to be an issue with how your app is registered. The first error usually happens when the app is not marked as multi-tenant, and you login to the app with a tenant other than the one where the app is registered.
The second error is odd. Client secret is what you're reading out of the ida:Password element and passing in the ClientCredential object.
I just put a .NET tutorial up yesterday that walks through setting this stuff up. Take a look and see if that helps get you unblocked.

HttpClient Task was cancelled

I am trying to use HttpClient to asynchronously log a message to a REST service using the code below:
public void LogMessage(string operationURI, string message, EventLogEntryType logEntryType)
{
using (var client = new HttpClient())
{
var cancellationToken = new CancellationToken();
client.SendAsync(GetRequest(operationURI), cancellationToken).ContinueWith(
cw =>
{
var response = cw.Result; //(I get an error on this line)
if (!response.IsSuccessStatusCode)
{
LogMessageLocal(message, logEntryType);
}
});
}
}
Note: The GetRequestMessage returns an HttpRequestMessage.
But I get an error stating 'A task was canceled.'
Any ideas?
I believe this can occur when the timeout is exceeded. You might check your timeout, and log how long it was outstanding before the exception to see if it is being exceeded.
The HttpClient is disposed before SendAsync finishes. This causes a TaskCanceledException to be thrown.
Add async keyword to LogMessage.
Add await keyword to SendAsync and set its result to var response.
Do whatever you want to do with the response after awaiting it.

Resources