Xamarin.Forms how to call async Task in App OnStart() - xamarin.forms

I try to do some chechking everytime when user try to start the application. This is my code example:
protected override async void OnStart()
{
// Handle when your app starts
var user = await FinDataStore.GetUserToken(DependencyService.Get<ISharedFunctions>().GetUser().UserName, DependencyService.Get<ISharedFunctions>().GetUserPassword());
if (user != null && user.AccessToken != null)
{
DependencyService.Get<ISharedFunctions>().SaveAccessToken(user.AccessToken);
DependencyService.Get<ISharedFunctions>().SaveUser(user);
DependencyService.Get<ISharedFunctions>().SaveRefreshToken(user.RefreshToken);
DependencyService.Get<ISharedFunctions>().SaveUserFirmi(user.Firmi);
}
else
{
((App)Application.Current).Logout();
}
}
but i get the error:
Error CS0120 An object reference is required for the non-static field, method, or property 'FinDataStore.GetUserToken(string, string)'
This is the call :
public async Task<User> GetUserToken(string username, string password)
How to solve this?

It's seems not to be a async problem.
It seems you should do something like:
var myclass = new FinDataStore();
then you can
var ret = await myClass.GetUserToken...

Related

Run a Task without killing

I'm trying to implement a functionality where user can simply click on download button and can go on any page that he/she wishes while download is still running, and it should not stop.
I'm using xamarin form.
Is there any way to do this, please suggest or any reference, since i couldn't find the same.
Since the class App always exists in memory when the application is running , we can place the Task in App.
Simple Task
//App
public async void DoSomething()
{
await Task.Delay(30000);
}
// in specific page
string result = await (App.Current as App).DoSomething();
Task with return values
//App
public async Task<string> DoSomething()
{
await Task.Delay(30000);
return "123";
}
// in specific page
string result = await (App.Current as App).DoSomething();
If you don't want to put the code into App , we can create a extra class in App to handle this .
//App
private MyTask _myTask;
public MyTask myTask
{
get
{
return _myTask ?? new MyTask();
}
}
//MyTask
public class MyTask
{
public async Task<string> DoSomething()
{
await Task.Delay(30000);
return "123";
}
}
//in specific page
string result = await (App.Current as App).myTask.DoSomething();

fetching user data from firebase and storing it in static variables

i am new to flutter and firebase development, so i really don't know how much will it cost me to keep fetching user data from firebase in every screen that i need them in, so i decided to fetch them once and store them in class MyUser static variables as follows:
in MyApp class:
bool isAuthenticated = false;
Future checkAuthenticity() async {
AuthService.getCurrentUser().then((user) async {
if (user != null) {
String myUid = await AuthService.getCurrentUID();
await MyUserController().getCurrentUserFromFirebase(myUid);
if (mounted)
setState(() {
isAuthenticated = true;
});
} else {
if (mounted)
setState(() {
isAuthenticated = false;
});
}
});
}
#override
Widget build(BuildContext context) {
home: isAuthenticated ? Home(passedSelectedIndex: 0) : Register(),
}
from the above code, this line await MyUserController().getCurrentUserFromFirebase(myUid); is as follows:
getCurrentUserFromFirebase(String uid) async {
await FirestoreService().getCurrentUserData(uid);
}
from the above code, this line await FirestoreService().getCurrentUserData(uid); is as follows:
Future getCurrentUserData(String uid) async {
try {
var userData = await FirebaseFirestore.instance.collection('users').doc(uid).get();
MyUser.fromData(userData.data());
} catch (e) {
if (e is PlatformException) {
return e.message;
}
return e.toString();
}
}
from the above code, this line MyUser.fromData(userData.data()); is a constructor in
MyUser class as follows:
class MyUser {
static String uid;
static String name;
static String username;
static String email;
static String userAvatarUrl;
static String location;
static String phoneNumber;
MyUser.fromData(Map<String, dynamic> data) {
uid = data['id'];
name = data['name'];
username = data['username'];
email = data['email'];
userAvatarUrl = data['userAvatarUrl'];
location = data['location'];
phoneNumber = data['phoneNumber'];
}
}
and to make use of all of the following, in each page that i need to load the current user data in, i use for example:
var userId = MyUser.uid
or to show the current user name i use Text('${MyUser.name}');
when i close the app completely and relaunch it again, it should check for authenticity, and complete executing the rest of the code in main() function.
so my questions are:
1) does this have any performance issues when we release the app?
2) does this will really will prevent unnecessary reads that i can consume in every page i need the data in ?
3) is there any better approach to prevent unnecessary reads from firebase, for example to save the current user data as strings and a profile image locally?
pardon me for prolonging the question, but i wanted to share the code itself.
any help would be much appreciated.
As a short answer,
You can make a class of SharedPreferences to store data as strings in key: value manner.
So anywhere you want you can get an instance of that class and reach it from anywhere in the app.
If you also declare some functions which will decode string to json you will get a ready user class instance in return of your function which will make it easier.
So when you want to save user info to Local Storage(SharedPreferences) you may use a function which will encode your User object to string and save it to SharedPreferences as below..
user.dart' as theUser; for conflict issues
class SharedPrefs {
static SharedPreferences _sharedPrefs;
init() async {
if (_sharedPrefs == null) {
_sharedPrefs = await SharedPreferences.getInstance();
}
}
dynamic get user=> _sharedPrefs.getString('user')!=null?theUser.User.fromString(_sharedPrefs.getString('user')):null;
set user(theUser.User user)=> _sharedPrefs.setString('user', jsonEncode(user));
String get accessToken=> _sharedPrefs.getString('access_token');
set accessToken(String accessToken)=> _sharedPrefs.setString('access_token', accessToken);
void removeString(String entry){
_sharedPrefs.remove(entry);
}
}
final sharedPrefs = SharedPrefs();
And in the app anywhere you can use it directly by typing sharedPrefs.user

ASP.NET Core - getting a message back from AuthenticationHandler

I have implemented a subclass of AuthenticationHandler. It returns AuthenticationResult.Fail("This is why you can't log in");
I would have expected this message to end up in the body, or at least in the HTTP status text, but instead I get a blank 401 response.
Is there any way to provide additional information for failed authentication attempts in ASP.NET core?
Override HandleChallengeAsync:
In the example below the failReason is a private field in my implementation of AuthenticationHandler.
I don't know if this is the best way to pass the reason for failure. But the AuthenticationProperties on the AuthenticateResult.Fail method did not make it through to HandleChallengeAsync in my test.
public class CustomAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
{
private string failReason;
public CustomAuthenticationHandler(IOptionsMonitor<TOptions> options
, ILoggerFactory logger
, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { }
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
failReason = "Reason for auth fail";
return AuthenticateResult.Fail(failReason);
}
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = 401;
if (failReason != null)
{
Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = failReason;
}
return Task.CompletedTask;
}
}
From the docs: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-2.2
Override this method to deal with 401 challenge concerns, if an authentication scheme in question deals an authentication interaction as part of it's request flow. (like adding a response header, or changing the 401 result to 302 of a login page or external sign-in location.)
Source:
https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs#L201
I used this code in my custom Middleware to return problemDetails response.
public async Task Invoke(HttpContext httpContext)
{
await this.Next(httpContext);
if (httpContext.Response.StatusCode == StatusCodes.Status401Unauthorized)
{
var authenticateResult = await httpContext.AuthenticateAsync();
if (authenticateResult.Failure != null)
{
var routeData = httpContext.GetRouteData() ?? new RouteData();
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
var problemDetails = this.ProblemDetailsFactory.CreateProblemDetails(httpContext,
statusCode: httpContext.Response.StatusCode,
detail: authenticateResult.Failure.Message);
var result = new ObjectResult(problemDetails)
{
ContentTypes = new MediaTypeCollection(),
StatusCode = problemDetails.Status,
DeclaredType = problemDetails.GetType()
};
await this.Executor.ExecuteAsync(actionContext, result);
}
}
}
For changing the body or Http status, you could try Context.Response.
Here is a demo code:
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace TestIdentity
{
public class CustomAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
{
public CustomAuthenticationHandler(IOptionsMonitor<TOptions> options
, ILoggerFactory logger
, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
await Context.Response.WriteAsync("This is why you can't log in");
return AuthenticateResult.Fail("This is why you can't log in");
}
}
}

Challenge() always redirects to Login Page

I am trying to authorize my Action method. Everything works fine but my Challenge() method always redirect to Login Page even if I've already logged in. I try to return Forbid() method and it redirects to Access Denied page as it should. What might be the problem with Challenge() method ?
public async Task<IActionResult> Edit(int id)
{
var project = await _context.Project.Include(p => p.OrganizationsLink).FirstOrDefaultAsync(p => p.Id == id);
if (project == null)
return NotFound();
//AUTHORIZATION
var allowed = await _authz.AuthorizeAsync(User, null, new ProjectEditRequirement(project));
if (!allowed.Succeeded)
return Challenge();
return View(project);
}
For Challenge, it is controlled by IAuthenticationService. AuthenticationService will call AuthenticateAsync to invoke the handler.AuthenticateAsync().
Not sure whether you implement custom IAuthenticationHandler, I will go deep the CookieAuthenticationHandler.
The complete workflow for CookieAuthenticaiton is below:
return Challenge() , for Challenge
public virtual ChallengeResult Challenge()
=> new ChallengeResult();
ChanllengeResult will call ExecuteResultAsync
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<ChallengeResult>();
logger.ChallengeResultExecuting(AuthenticationSchemes);
if (AuthenticationSchemes != null && AuthenticationSchemes.Count > 0)
{
foreach (var scheme in AuthenticationSchemes)
{
await context.HttpContext.ChallengeAsync(scheme, Properties);
}
}
else
{
await context.HttpContext.ChallengeAsync(Properties);
}
}
context.HttpContext.ChallengeAsync will invoke ChallengeAsync
public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().ChallengeAsync(context, scheme, properties);
For CookieAuthenticationHandler
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
var redirectUri = properties.RedirectUri;
if (string.IsNullOrEmpty(redirectUri))
{
redirectUri = OriginalPathBase + Request.Path + Request.QueryString;
}
var loginUri = Options.LoginPath + QueryString.Create(Options.ReturnUrlParameter, redirectUri);
var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(loginUri));
await Events.RedirectToLogin(redirectContext);
}
As you could find, the HandleChallengeAsync redirect the action.
In my option, you could try return Forbid(), otherwise, you will need to override the HandleChallengeAsync.

usermanager.addtorole - An asynchronous module or handler completed while an asynchronous operation was still pending

I am adding a user to my aspnetusers database and that is working fine. Then I am also trying to link them to an existing role. That is when I get the error: "An asynchronous module or handler completed while an asynchronous operation was still pending."
Here is my method with the problem code:
private async void checkOldDB(string email, string password)
{
bool isValidUser = false;
ReportsMvc.App_Code.BLL.FUN.cFunUser user = ReportsMvc.App_Code.DAL.FUN.cFunUserDB.getUser(email);
if (user != null)
{
isValidUser = PasswordHash.PasswordHash.ValidatePassword(password, user.Password);
if (!isValidUser)
{
isValidUser = PasswordHash.PasswordHash.ValidateHashes(password, user.Password);
}
}
if (isValidUser)
{
var user2 = new ApplicationUser { UserName = email, Email = email };
var result = await UserManager.CreateAsync(user2, password);
if (result.Succeeded)
{
string role = user.Role;
if (string.IsNullOrEmpty(role))
{
role = "User";
}
UserManager.AddToRole(user2.Id, role);
await SignInManager.SignInAsync(user2, isPersistent: false, rememberBrowser: false);
}
}
}
The line starting with "await SignInManager" was working fine. Then when I added in that code to AddToRole, I started getting the above error. This identity/authentication stuff is all very new to me.
You should change async void to async Task and await it where you call it.
As a general rule, you should avoid async void; it should only be used for event handlers. I describe this more in an MSDN article.

Resources