0
votes

I have a Xamarin.forms application with an Azure Mobile Apps backend where I have tried to use Enterprise ans Social authentication following this tutorial https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/ .

I've been able to set up server side both of them and test that they are working. It can be tested in these pages 

https://tasklistsync2.azurewebsites.net/.auth/login/google https://MyMobileApps.b2clogin.com/MyMobileApps.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_emailPolicy&client_id=9ec9bcd1-ee5f-4abb-9908-8d63a24b6165&nonce=defaultNonce&redirect_uri=https%3A%2F%2Ftasklistsync2.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback&scope=openid&response_type=id_token&prompt=login

I've also included the code in my mobile app to use this authentication flow and it works till the point that I enter my credentials the page reloads but then it stays blank and never comes back to my mobile app as login successfull.

This is what happens when I click the login button.

async Task ExecuteLoginCommand()
        {
            if (IsBusy)
                return;
            IsBusy = true;

            try
            {
                var cloudService = ServiceLocator.Instance.Resolve<ICloudService>();

                //Login methods

                await cloudService.LoginAsync();           //Social/enterprise
                //await cloudService.LoginAsync(User);      //Custom login

                Application.Current.MainPage = new NavigationPage(new Pages.TaskList());
            }
            catch(Exception ex)
            {
                Debug.WriteLine($"[Login] Error = {ex.Message}");
            }
            finally
            {
                IsBusy = false;
            }
        }

It goes to cloudService.LoginAsync() that is implemented in my AzureCloudService class.

public Task LoginAsync()
        {
            var loginProvider = DependencyService.Get<ILoginProvider>();
            return loginProvider.LoginAsync(client);
        }

Also implemented in the android project.

public async Task LoginAsync(MobileServiceClient client)
        {            
            await client.LoginAsync(context, MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, "tasklistsync2");
        }

So all this process gets executed and I'm able to enter my credentials but then the app stays there, with the login page blank and the new NavigationPage(new Pages.TAskList()); line never gets exectued. If I close the login page, it goes to the finally block in the Try and Catch.

I think the missing piece is to finish the login process and come back to the mobile application but I'm not sure how can I make this happen (I believe it should be happening already).

The reply URL for the Authentication Processes set up in the Azure configuration as explained in the tutorial are https://tasklistsync2.azurewebsites.net/.auth/login/google/callback https://tasklistsync2.azurewebsites.net/.auth/login/aad/callback

1

1 Answers

0
votes

You need to implement login interfaces in platform specific for iOS and Android.

 public class iOSPlatform : IPlatform {

            public UIViewController RootView => UIApplication.SharedApplication.KeyWindow.RootViewController;

            public AccountStore AccountStore { get; private set; }

            public iOSPlatform()
            {
                AccountStore = AccountStore.Create();
            }
  public async Task<MobileServiceUser> LoginAsync(MobileServiceClient client)
        {
            var accessToken = await LoginADALAsync();
            var zumoPayload = new JObject();
            zumoPayload["access_token"] = accessToken;
            if (accessToken != null)
                return await client.LoginAsync("aad", zumoPayload);
            else
                return null;
        }

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
        public async Task LogoutAsync()
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
        {
            // Do nothing
            var authContext = new AuthenticationContext(Constants.authority);
            if (authContext.TokenCache.ReadItems().Any())
            {
                authContext.TokenCache.Clear();
            }

            //In addition to clearing the token cache, you should also clear the cookies in the web view.
            //Otherwise, the session cookies come into play and that is why you are seeing the web view come up and disappear immediately.
            foreach (var cookie in NSHttpCookieStorage.SharedStorage.Cookies)
            {
                NSHttpCookieStorage.SharedStorage.DeleteCookie(cookie);
            }
        }

        private async Task<string> LoginADALAsync()
        {
            var authContext = new AuthenticationContext(Constants.authority);
            if (authContext.TokenCache.ReadItems().Any())
            {
                authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
            }
            var authResult = await authContext.AcquireTokenAsync(
                Constants.resourceId,
                Constants.clientId,
                new Uri(Constants.redirectUri),
                new PlatformParameters(RootView));
            return authResult.AccessToken;
        }
}

Similar for Android, except Android uses Context instead of UIViewController

public Context RootView { get; private set; }

        public AccountStore AccountStore { get; private set; }

        public void Init(Context context)
        {
            RootView = context;
            AccountStore = AccountStore.Create(context);
        }