Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xamarin: ApplicationContext.User not persisting during Async call #1010

Closed
JamesJohn93 opened this issue Nov 23, 2018 · 12 comments
Closed

Xamarin: ApplicationContext.User not persisting during Async call #1010

JamesJohn93 opened this issue Nov 23, 2018 · 12 comments
Labels

Comments

@JamesJohn93
Copy link

Description of the Issue
During an async call I am getting an UnauthenticatedPrincipal whenever I try to use Csla.ApplicationContext.User set to Csla.Principal to Check if a user is authenticated even though I have set it just after authenticating the user.

Version and Platform
CSLA version: 4.8.1
OS: Windows.
Platform: ASP.NET Standard 2.0, Xamarin Forms, Android. IOS.

Code that Fails
Failure occurs in the Business Object when I try to retrieve any data.

Login Command

    private async void OnLogin()
    {
        IsBusy = true;
        if (_connectionService.IsConnected)
        {
            var isAuthenticated = await _authenticationService.Authenticate(UserName, Password); //at this point the user is already Authenticated

            if (isAuthenticated)
            {
                _dialogService.ShowToast($"Successfully logged in {(Csla.ApplicationContext.User as CSLAPrincipal).Identity.Name}"); //This Line works.
                IsBusy = false;
                NavigateToAsync(HomeViewModel); //Navigate to Home Screen Async. This is where Csla.ApplicationContext.User is now returning UnAuthenticatedPrincipal.

            }
            else
                //Show some Error
        }
        else
        {
            //Show another Error
        }
    }

AuthenticationService.Authenticate Method called in the LoginCommand

    public async Task<bool> Authenticate(string userName, string password)
    {
        return await MyPrincipal.LoginAsync(userName, password);
    }

LoginAsync Method called in Authenticate Method

    public static async Task<bool> LoginAsync(string username, string password)
    {

        //Validate Password and Give User appropriate feedback
        
        return (await VerifyIdentityAsync(username, password)).IsAuthenticated;
    }

Getting UserIdentity and Setting ApplicationContext.User

    private static async Task<MyIdentity> VerifyIdentityAsync(string userName, string passWord)
    {
        MyIdentity identity = await MyIdentity.GetIdentityAsync(userName, passWord);

        if (identity.IsAuthenticated)
        {
            MyPrincipal principal = new MyPrincipal(identity);
            Csla.ApplicationContext.User = principal;
        }

        return identity;
    }

Home Screen Init Method Where Failure occurs

	public class HomeViewModel 
	{
		private string personNameProperty;
		public string PersonName
		{
			get => personName;
			set
			{
				personName = value;
				OnPropertyChanged();
			}
		}

		public override async Task InitializeAsync(object data)
		{
			PersonNameProperty = Csla.ApplicationContext.User.Identity.UserName; //Also Failing because we are trying to Access ApplicationContext.User but it contains an UnAuthenticatedPrincipal.
			
			var info = InfoCslaBussinessObject.GetInfoAsync(); //Failing because of UnAuthenticated Principal returned.
			info.AddCommentToInfo("Some interesting information");
			info.Save();
			//Do some interesting stuff with data and info.
		}
	}	

AddCommentToInfo Method

    public void AddComment(string comment)
    {
        if (comment != string.Empty)
        {
            if (Comments != string.Empty)
                Comments += Environment.NewLine + Environment.NewLine;
            Comments += "{" + Utils.Now.Text + ":" + Csla.ApplicationContext.User.Identity.Name + "} " + Environment.NewLine + comment;
        }
    }

Exception Detail and Additional Context
Even If I try to use the Csla.ApplicationContext.User elsewhere in the Home Screen View Model, I still get an UnauthenticatedPrincipal as seen in the AddToComment(). Csla.ApplicationContext.User.Identity.Name is null.

@rockfordlhotka
Copy link
Member

You have a reference to the CSLA-ASP.NET NuGet package?

@rockfordlhotka
Copy link
Member

btw, for faster response to this sort of question in the future, please post in the CSLA .NET forum instead of in the main repo. A lot of people watch the forum, this repo's issues are typically only checked when I'm grooming the backlog.

@JamesJohn93
Copy link
Author

I have it referenced in my App Server but not in my Business Library, and my App PCL. When I try to reference it I get the following error:
Error NU1202: Package CSLA-ASP.NET 4.8.1 is not compatible with monoandroid81 (MonoAndroid,Version=v8.1). Package CSLA-ASP.NET 4.8.1 supports:

  • net40 (.NETFramework,Version=v4.0)
  • net45 (.NETFramework,Version=v4.5)
  • net461 (.NETFramework,Version=v4.6.1) Mobile.Android C:..\Projects\ Mobile\Mobile\Mobile.Android\Mobile.Android.csproj 1

The same error is Replicated for my iOS project, except the incompatibility is now between CSLA-ASP.NET 4.8.1 and xamarinios10.

Also, thanks for the tip.

@rockfordlhotka
Copy link
Member

I was only referring to the server project. It isn't surprising that ASP.NET code is incompatible with Xamarin.

On the client side you need to make sure to reference CSLA-Xamarin.

The reason I asked about the server, is that proper handling of the User property requires per-platform support, and the CSLA-ASP.NET package contains that code.

@JamesJohn93
Copy link
Author

I see.

CSLA-XamarinForms is referenced in all of my UI projects.

@MTantos
Copy link

MTantos commented Dec 4, 2018

I believe we are having the same problem with one of our applications.
I will preface this with the fact that everything was working fine until I tried to update the app a few days ago.

I believe the root cause to be something having changed with Xamarin. I am inclined to believe this as:

  • We have a working app which was publish to the app store October 19.
  • Since then we have updated VS2017 to 15.9.3 (which includes Xamarin version 4.12.3.73, and Xamarin.iOS/Mac SDK 12.2.1.11)
  • I have rebuilt our application (the exact code base which was previously compiled and submitted to app store) only to find that Csla.ApplicationContext.User is now always UnathenticatedPrincipal.

Further on this point, I must also set the Linker Behavior (iOS app properties -> iOS Build) to "Don't Link" otherwise Csla.ApplicationContext.User = new Csla.Security.UnauthenticatedPrincipal(); throws System.NotSupportedException: Linked Away (The mono linker seems to optimize away Csla.Security.UnauthenticatedPrincipal for whatever reason). Previously we have always set Linker Behavior to "Link Framework SDKs Only" and this has worked fine.

@JamesJohn93
Copy link
Author

@MTantos Have you tried to upgrade to .NET 4.7.2? There were a few Xamarin related fixes in the update from Microsoft.

@MTantos
Copy link

MTantos commented Dec 4, 2018

@JamesJohn93 No I haven't. I am sceptical that this will solve my problem as the server is working fine; running .NET 4.6.1. The client, using XamarinForms (.netstandard 2.0.3), and Xamarin (iOS 12.1) is where the issue lies.

In the following code blocks (which run on the client), the identity is correctly returned from the server, but the client fails to set Csla.ApplicationContext.User on the worker thread and retrieve it again on the Main Thread.

Principal

public static async Task<bool> LoginAsync(string username, string password)
{
    try
    {
        var identity = await TAIdentity.GetTAIdentityAsync(username, password);
        // identity is correctly returned here.
        var isAuth = SetPrincipal(identity);
        var user = Csla.ApplicationContext.User;
        // user is Correctly TAPrincipal here
        return isAuth;
    }
    catch(Exception ex)
    {
        Logout();
    }
    return false;
}

private static bool SetPrincipal(IIdentity identity)
{
    if (identity.IsAuthenticated)
    {
        TAPrincipal principal = new TAPrincipal(identity);
        // principal is correctly instantiated here
        Csla.ApplicationContext.User = principal;
        var user = Csla.ApplicationContext.User;
        // user is correctly a TAPrincipal here
    }
    OnNewUser();
    return identity.IsAuthenticated;
}

public static void Logout()
{
    Csla.ApplicationContext.User = new UnauthenticatedPrincipal();
    OnNewUser();
}

Calling Code

var result = await Principal.LoginAsync(Username.Text, Password.Text);
// result is true (successfull login)
var user = Csla.ApplicationContext.User;
// user is Csla.Security.UnauthenticatedPrincipal

If I replace Csla.ApplicationContext.User with ApplicationContext.User where ApplicationContext is define as below. The user above is persisted correctly between threads.

public static class ApplicationContext
{
    public static IPrincipal User { get; set; }
}

With all that being said; I'll try updating my server to .NET 4.7.2 and report back if that does indeed solve my issue.

@MTantos
Copy link

MTantos commented Dec 5, 2018

As a temporary work around I have found the following to work.

Define the following class

public class MyApplicationContextManager : Csla.ApplicationContext.ApplicationContextManager
{
    public static IPrincipal _principal;

    public override IPrincipal GetUser() { return _principal  ?? new Csla.Security.UnauthenticatedPrincipal(); }

    public override void SetUser(IPrincipal principal) { _principal = principal; }
}

And then in App.xaml.cs

protected override void OnStart()
{
    // Handle when your app starts
    Csla.ApplicationContext.DataPortalProxy = typeof(Csla.DataPortalClient.HttpProxy).AssemblyQualifiedName;
    Csla.ApplicationContext.DataPortalUrlString = "path/to/your/server";
    Csla.ApplicationContext.ContextManager = new MyApplicationContextManager();

    Library.Security.TAPrincipal.Logout();
}

@rockfordlhotka
Copy link
Member

It appears that CSLA isn't properly loading the appropriate context manager from Csla.Xaml like it should.

I encountered this a couple weeks ago, but in a solution with WebAssembly as well as Xamarin, and I blamed it on the wasm project in the solution. Perhaps there really is something preventing CSLA from auto-detecting and loading the context manager from Csla.Xaml in Xamarin.

@rockfordlhotka rockfordlhotka changed the title ApplicationContext.User not persisting during Async call Xamarin: ApplicationContext.User not persisting during Async call Apr 2, 2019
@rockfordlhotka
Copy link
Member

Resolved by #1043

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 10, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants