0
votes

i am trying to make Azure AD B2C work in my xamarin app. to check if we already are logged in i am using a code like below.

  public async Task<bool> IsLoggedIn()
    {            
            IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
            if (accounts != null)
            {
                return accounts.Any();
            }
            return false;
        }
    }

My code on Start looks like this

 protected async override void OnStart()
    {

            var authenticationService = Container.Resolve<IAuthenticationService>();
            bool isLoggedIn;

            isLoggedIn = await authenticationService.IsLoggedIn();

            if (isLoggedIn)
            {
                var cachingService = Container.Resolve<ICachingService>();

                AppUser = await authenticationService.Login(cachingService.CurrentMode);

                await NavigationService.NavigateAsync("http://www.xxxx.com/root/navigation/main");
            }
            else
            {
                await NavigationService.NavigateAsync("http://www.xxxx.com/navigation/login");
            }
        }           
    }

if i just return false from IsLoggedIn it shows the login page properly. But calling

IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();

seems to be problematic.

i get exception (sometimes) that System.NullReferenceException: Object reference not set to an instance of an object

but looking at App object in "QuickWatch" i see PAC object being properly populated.

UPDATE App.cs constructor looks like this

public static PublicClientApplication PCA = null;
 public App(IPlatformInitializer initializer = null) : base(initializer)
    {
        PCA = new PublicClientApplication(GlobalSetting.Instance.ClientID, GlobalSetting.Instance.AuthoritySignUpSignIn);
        PCA.RedirectUri = $"msal{GlobalSetting.Instance.ClientID}://auth";
    }

my login method looks like this

    public async Task<User> Login(string mode)
    {
        IEnumerable<IAccount> accounts = await App.PCA?.GetAccountsAsync();
        AuthenticationResult ar;
        try
        {   //get token from cache.
            ar = await App.PCA?.AcquireTokenSilentAsync(GlobalSetting.Instance.Scopes, GetAccountByPolicy(accounts, GlobalSetting.Instance.PolicySignUpSignIn), GlobalSetting.Instance.AuthoritySignUpSignIn, false);
        }
        catch (MsalUiRequiredException ex)
        {
            // get token from interaction.
            ar = await App.PCA?.AcquireTokenAsync(GlobalSetting.Instance.Scopes, GetAccountByPolicy(accounts, GlobalSetting.Instance.PolicySignUpSignIn), App.UiParent);
        }
         //fetch token and make actual user object.
        return new User
            {
                Id = _cachingService.LoggedInUserId,
                Name = "Jessica Doe",
                ProfilePicUrl = "https://content-static.upwork.com/uploads/2014/10/01xxx27/profilephoto1.jpg",
                BusinessProfile = _userService.GetBusinessProfile(_cachingService.LoggedInUserId),
                ProfileVariables = _userService.GetUserProfileVariables(_cachingService.LoggedInUserId),
                Settings = _userService.GetSettings(_cachingService.LoggedInUserId)
            };

    }

it works when i dont call AcquireTokenSilentAsync and just send a fake User object and does not work when i call AcquireTokenSilentAsync. ar object gets populated, it navigates to the main page hits it's view model constructor but but shows a blank page.

i have also tried diffrent versions of MSAL. now on the latest version.

UPdate2

drilled down to this exception

{Microsoft.Identity.Client.MsalClientException: The application does not have keychain access groups enabled in the Entitlements.plist. As a result, there was a failure to save to the iOS keychain. The keychain access group '3G3LMCD5R.com.microsoft.adalcache' is not enabled in the Entitlements.plist. See https://aka.ms/msal-net-enable-keychain-groups for more details on enabling keychain access groups and entitlements. at Microsoft.Identity.Core.iOSTokenCacheAccessor.Save (System.String account, System.String service, System.String generic, System.Int32 type, System.String value) [0x00052] in :0 at Microsoft.Identity.Core.iOSTokenCacheAccessor.SaveAccessToken (Microsoft.Identity.Core.Cache.MsalAccessTokenCacheItem item) [0x00028] in :0 at Microsoft.Identity.Core.Telemetry.TelemetryTokenCacheAccessor.SaveAccessToken (Microsoft.Identity.Core.Cache.MsalAccessTokenCacheItem item) [0x00000] in :0 at Microsoft.Identity.Core.Telemetry.TelemetryTokenCacheAccessor.SaveAccessToken (Microsoft.Identity.Core.Cache.MsalAccessTokenCacheItem item, Microsoft.Identity.Core.RequestContext requestContext) [0x0002a] in :0 at Microsoft.Identity.Client.TokenCache.SaveAccessAndRefreshToken (Microsoft.Identity.Core.Instance.IValidatedAuthoritiesCache validatedAuthoritiesCache, Microsoft.Identity.Core.Instance.IAadInstanceDiscovery aadInstanceDiscovery, Microsoft.Identity.Client.Internal.Requests.AuthenticationRequestParameters requestParams, Microsoft.Identity.Core.OAuth2.MsalTokenResponse response) [0x00143] in :0 at Microsoft.Identity.Client.Internal.Requests.RequestBase.CacheTokenResponseAndCreateAuthenticationResult (Microsoft.Identity.Core.OAuth2.MsalTokenResponse msalTokenResponse) [0x001b4] in :0 at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest+d__9.MoveNext () [0x00168] in :0 --- End of stack trace from previous location where exception was thrown --- at Microsoft.Identity.Client.Internal.Requests.RequestBase+d__28.MoveNext () [0x00160] in :0 --- End of stack trace from previous location where exception was thrown --- at Microsoft.Identity.Client.PublicClientApplication+d__24.MoveNext () [0x000ef] in :0 --- End of stack trace from previous location where exception was thrown --- at Microsoft.Identity.Client.PublicClientApplication+d__17.MoveNext () [0x000ac] in :0 --- End of stack trace from previous location where exception was thrown --- at CDThat.Services.AzureADB2CAuthenticationService+d__3.MoveNext () [0x000fc] in C:\CDthatbest\CDThat\Services\AzureADB2CAuthenticationService.cs:45 --- End of stack trace from previous location where exception was thrown --- at CDThat.ViewModels.LoginPageViewModel+d__8.MoveNext () [0x0003c] in C:\CDthatbest\CDThat\ViewModels\LoginPageViewModel.cs:56 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b__6_0 (System.Object state) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.2.1.11/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1023 at Foundation.NSAsyncSynchronizationContextDispatcher.Apply () [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.2.1.11/src/Xamarin.iOS/Foundation/NSAction.cs:178 at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr) at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.2.1.11/src/Xamarin.iOS/UIKit/UIApplication.cs:79 at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0002c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.2.1.11/src/Xamarin.iOS/UIKit/UIApplication.cs:63 at CDThat.iOS.Application.Main (System.String[] args) [0x00014] in C:\CDthatbest\CDThat.iOS\Main.cs:16 ErrorCode: missing_entitlements}

1

1 Answers

0
votes

It might be a timing issue (accessing the PCA property to early). I would first change the code to handle the empty PCA property more gracefully:

public async Task<bool> IsLoggedIn()
{            
        // Added a null check on the PCA property
        IEnumerable<IAccount> accounts = await App.PCA?.GetAccountsAsync();
        if (accounts != null)
        {
            return accounts.Any();
        }
        return false;
    }
}

And then try a different event (such as PageAppearing) instead of OnStart to access the PCA property.

Hope it helps!