locked
MSAL Authentication Does Not Complete RRS feed

  • Question

  • User235233 posted

    I'm trying to get my feet wet with Xamarin and I'm having trouble adding in my organization's login. The screen shot below is as far as I can get attempting to login. When I click "Continue" the same page just loads again. Not really sure what's going on.

    The image is the screen I'm stuck on.

    I've added code that represents the app class and the code behind for the XAML page attempting to login, leaving out what I "think" is irrelevant.

    Any suggestions?

    App class...

    public partial class App : Application
         {
             public static string AzureBackendUrl =
                 DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:5000";
             public static bool UseMockDataStore = true;
             public static IPublicClientApplication PCA = null;
             public static string ClientID = "CLIENT_ID";
             public static string[] Scopes = { "User.Read" };
             public static string Username = string.Empty;
             public static object ParentWindow { get; set; }
             public App()
             {
                 InitializeComponent();
                 if (UseMockDataStore)
                     DependencyService.Register<MockDataStore>();
                 else
                     DependencyService.Register<AzureDataStore>(); 
                 PCA = PublicClientApplicationBuilder.Create(ClientID)
                     .WithRedirectUri($"msal{App.ClientID}://auth")
                     .Build();
                 MainPage = new MSAL_Example();
             }
         }
    

    MSAL_Example class...

    public partial class MSAL_Example : ContentPage
     {
         public static string tenant_name = "MY_TENANT_NAME"; 
         public MSAL_Example()
         {
             InitializeComponent();
             App.ParentWindow = this;
         }
         public async Task SignOutAsync()
         {
             IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
             try
             {
                 while (accounts.Any())
                 {
                     await App.PCA.RemoveAsync(accounts.FirstOrDefault());
                     accounts = await App.PCA.GetAccountsAsync();
                 }
                 slUser.IsVisible = false;
                 Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign in"; });
             }
             catch (Exception ex)
             {
                 Debug.WriteLine("\tERROR {0}", ex.Message);
             }
         }
         public async Task SignInAsync()
         {
             AuthenticationResult authResult = null;
             IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync(); 
             // let's see if we have a user in our belly already
             try
             {
                 IAccount firstAccount = accounts.FirstOrDefault();
                 authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
                                       .ExecuteAsync();
                 await RefreshUserDataAsync(authResult.AccessToken).ConfigureAwait(false);
                 Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
             }
             catch (MsalUiRequiredException ex)
             {
                 try
                 {
                     authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
                                               .WithParentActivityOrWindow(App.ParentWindow)
                                               .WithAuthority("https://login.microsoftonline.com/" + tenant_name)
                                               .ExecuteAsync();
    
                     await RefreshUserDataAsync(authResult.AccessToken);
                     Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
                 }
                 catch (Exception ex2)
                 {
                     Debug.WriteLine("\tERROR {0}", ex2.Message);
                 }
             }
         } 
         public async Task RefreshUserDataAsync(string token)
         {
             //get data from API
             HttpClient client = new HttpClient();
             HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
             message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
             HttpResponseMessage response = await client.SendAsync(message);
             string responseString = await response.Content.ReadAsStringAsync();
             if (response.IsSuccessStatusCode)
             {
                 JObject user = JObject.Parse(responseString); 
                 slUser.IsVisible = true; 
                 Device.BeginInvokeOnMainThread(() =>
                 {
    
                     lblDisplayName.Text = user["displayName"].ToString();
                     lblGivenName.Text = user["givenName"].ToString();
                     lblId.Text = user["id"].ToString();
                     lblSurname.Text = user["surname"].ToString();
                     lblUserPrincipalName.Text = user["userPrincipalName"].ToString();
    
                     // just in case
                     btnSignInSignOut.Text = "Sign out";
                 });
             }
             else
             {
                 await DisplayAlert("Something went wrong with the API call", responseString, "Dismiss");
             }
         }
     }
    
    Thursday, May 28, 2020 4:33 PM

All replies

  • User2157 posted

    Is this using Azure ADB2C or similar? In which case the redirect URL is set at the other end in the App Registration you have setup in AD. For example in one of mine I have a redirect url of;

    msal1111111111-2222-3333-4444-55555555://auth. where the long number is the App Registration ID, sometimes called the client ID

    Friday, May 29, 2020 9:57 AM
  • User235233 posted

    I have set the redirect URL in Azure..

    And in my code I have it set in the App class...

    PCA = PublicClientApplicationBuilder.Create(ClientID)
                     .WithRedirectUri($"msal{App.ClientID}://auth")
                     .Build();
    

    I believe this is what you're talking about. Do I need to be doing something else with the url?

    Friday, May 29, 2020 12:34 PM
  • User2157 posted

    Looks about right, in my example I have in App.xaml.cs;

    AuthenticationClient = PublicClientApplicationBuilder.Create(SettingsViewModel.AadClientId)
                    .WithB2CAuthority(SettingsViewModel.AadSignInPolicyUrl)
                    .WithRedirectUri("msal" + SettingsViewModel.AadClientId + "://auth")
                    .Build();
    

    Then in my loginpage.xaml.cs in OnAppearing();

    try
                {
                    // Look for existing account
                    var accounts = await App.AuthenticationClient.GetAccountsAsync();
    
                    //Logout
                    while (accounts.Any())
                    {
                        await App.AuthenticationClient.RemoveAsync(accounts.First());
                        accounts = await App.AuthenticationClient.GetAccountsAsync();
                    }
    
                    var result = await App.AuthenticationClient
                        .AcquireTokenSilent(SettingsViewModel.AadScopes, accounts.FirstOrDefault())
                        .ExecuteAsync();
                }
                catch
                {
                    // Do nothing - the user isn't logged in
                }
    

    And also within my login button .Clicked command handler;

    AuthenticationResult result;
                    result = await App.AuthenticationClient
                        .AcquireTokenInteractive(SettingsViewModel.AadScopes)
                        .WithPrompt(Prompt.SelectAccount)
                        .WithParentActivityOrWindow(App.UIParent)
                        .ExecuteAsync();
    
                    var jwt = new JwtSecurityToken(result.IdToken);
    

    At the point we get to the last line it's done it's redirect, authenticated, and redirected back

    Friday, May 29, 2020 2:05 PM
  • User395506 posted

    Not sure if you are still stuggling on this but in the main activity you have to set the contiunation helper. Let me know if you need it and I can provide the code

    Wednesday, July 1, 2020 8:36 PM
  • User235233 posted

    @ryanlafountain I am still stuck on this,and I would appreciate any code or help you can offer.

    Wednesday, July 1, 2020 9:47 PM
  • User395506 posted

    @mcconnelljh , can you post where the SignInAsync is being called from? Also, can you post the MainActivity.cs OnActivityResult override code. Can you also post the AndroidManifest.xml.

    Thursday, July 2, 2020 12:19 AM
  • User400504 posted

    I am having the same issue, did you find the solution in the end?

    Saturday, April 10, 2021 8:36 AM