none
Xamarin UWP Client Managed Authorization RRS feed

  • Question

  • I am currently building a Xamarin App and I want to use client managed Authorization/Authentication. I have an Azure Mobile Apps backend set up with Azure Active Directory Authentication. I am using Microsoft Identity Client NuGet Package in my code; however, the access-token that gets returned form that, is not valid when I call client.loginAsync(). Below is the code I am using to retrieve the access token. Please Help. All of the documentation online seems to either out of date, or contradicts itself. 

                        authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
                                                                   .ExecuteAsync();

    My redirect URL in my active directory registration is - urn:ietf:wg:oauth:2.0:oob


    Sunday, August 11, 2019 10:46 PM

All replies

  • Hi Ben,

    What sort of validation error message do you get after calling client.loginAsync()? Auth is tangled weave so any details you can provide would be helpful. Such as the scopes you're using, did you use EasyAuth for your app? What roles did you configure for app registration? Etc.

    Also, did you follow any online step-by-step tutorials? This would be helpful in improving our docs so folks such as yourself don't run into issues. Looking forward to your response.


    Thanks in advance, Ryan

    Monday, August 12, 2019 2:30 PM
    Moderator
  • Sure Ryan, Thanks for responding so quickly.

    So the first setup that I did was for an Azure Active Directory. I simply created a new App Registration, and am using "Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)" to get public accounts.

    Then under "Authorization" I set up the redirects as follows:

    Web - https://diveapp.azurewebsites.net/.auth/login/aad/callback

    Public client (Mobile - Desktop) - urn:ietf:wg:oauth:2.0:oob

    Lastly, for the Active Directory, under "Authorization", I set the implicit grant to both access Tokens and id Tokens.

    Then I went into my Mobile App (Which I have set up and is working without Authentication. I am able to save data to a SQL Table), and I began to set up Authentication. I went to the Authentication / Authorization tab, and turned "App service Authentication" to on. I then configured the Auth as follows pointing to the registration from above. 

    Redirect URL - https://diveapp.azurewebsites.net/.auth/login/aad/callback

    Action to take when request is not Authenticated - Login with Azure Active Directory

    As for the Code, I went into the mobile service app, and added [Authorize] to my table controller. 

    Then inside of my actual Xamarin Mobile app, I set the code up as follows. To get the AcessToken I use:

    //Set the scope for API call to user.read
            string[] scopes = new string[] { "user.read" };
    
            // Below are the clientId (Application Id) of your app registration and the tenant information. 
            // You have to replace:
            // - the content of ClientID with the Application Id for your app registration
            // - Te content of Tenant by the information about the accounts allowed to sign-in in your application:
            //   - For Work or School account in your org, use your tenant ID, or domain
            //   - for any Work or School accounts, use organizations
            //   - for any Work or School accounts, or Microsoft personal account, use common
            //   - for Microsoft Personal account, use consumers
            private const string ClientId = "784838d9-3899-4cbd-a68f-84e7be7a499f";
            public IPublicClientApplication PublicClientApp;
    
            public Home()
            {
                this.InitializeComponent();
            }
    
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
                    .Build();
    
                AuthenticationResult authResult = null;
    
                // It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible.            
                IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync();
                IAccount firstAccount = accounts.FirstOrDefault();
    
                try
                {
                    authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
                                                           .ExecuteAsync();
                }
                catch (MsalUiRequiredException ex)
                {
                    //    A MsalUiRequiredException happened on AcquireTokenSilent.
                    // This indicates you need to call AcquireTokenInteractive to acquire a token
                    //System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
    
                    try
                    {
                        authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
                                                                   .ExecuteAsync();
                        //.ConfigureAwait(false);
                    }
                    catch (MsalException msalex)
                    {
                        //await DisplayMessageAsync($"Error Acquiring Token:{System.Environment.NewLine}{msalex}");
                    }
                }
                catch (Exception ex)
                {
                    //await DisplayMessageAsync($"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}");
                    return;
                }
    
                if (authResult != null)
                {
                    Settings.AuthToken = authResult.AccessToken;
                    Settings.UserId = authResult.UniqueId;
                }
            }


    Then when I am calling my service to fetch data I use:

    const string appUrl = "https://diveapp.azurewebsites.net";
                client = new MobileServiceClient(appUrl);
                await auth.AuthenticateAsync(client, MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory);

    And Lastly when calling the AuthenticateAsync()  method, this is the code:

            public async Task<bool> AuthenticateAsync(MobileServiceClient client, MobileServiceAuthenticationProvider provider)
            {
                string message;
                bool success = false;
                try
                {
                    // Change 'MobileService' to the name of your MobileServiceClient instance.
                    // Sign-in using AAD authentication.
                    var token = new JObject();
                    token.Add("access_token", Settings.AuthToken);
                    user = await client.LoginAsync(provider, token);
                    message = string.Format("You are now signed in - {0}", user.UserId);
    
                    success = true;
                }
                catch (InvalidOperationException)
                {
                    message = "You must log in. Login Required";
                }
                return success;
            }

    The error that I am getting is "Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in System.Private.CoreLib.dll"

    I am getting an AccessToken and an IdToken back in the AuthResult; however, If I log into my site directly https://diveapp.azurewebsites.net/.auth/login/aad/ I am getting a different AccessToken and IdToken and so that is why it is failing. If I take the AccessToken from the Website login, and hardcode it as the token value in my UWP app code, the loginAsync method works. 

    I followed several documents in trying to get this going. 

    1. https://docs.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-windows-store-dotnet-get-started-users

    This is the first link I used; however, the loginAsync() method in this document, does not require a token, and the current version of MobileServiceClient.loginAsync() requires a token. https://docs.microsoft.com/en-us/dotnet/api/microsoft.windowsazure.mobileservices.mobileserviceclient.loginasync?view=azure-dotnet

    2. https://docs.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-dotnet-how-to-use-client-library#clientflow

    I then used this document, to try and set up authentication; however, the PlatformBehaviors method used to build out the AquireTokenAsync() method, is using false. In the current version of PlatFormBehaviors(), the second parameter expects an ICustomWebUI. 

    public PlatformParameters(PromptBehavior promptBehavior, ICustomWebUi customWebUi);

    3. https://registeredapps.hosting.portal.azure.net/registeredapps/Content/1.0.00937231/Quickstarts/en/UwpQuickstartPage.html?appId=784838d9-3899-4cbd-a68f-84e7be7a499f&copy=Copy&tenantId=common&tenantName=Default%20Directory&signInAudience=AzureADandPersonalMicrosoftAccount&clientOptimizations=undefined&l=en.en-us&trustedAuthority=https%3A%2F%2Fportal.azure.com&shellVersion=undefined

    I have looked for other documentation, but it all seems to be outdated, or contradicts itself. I know this was a lot, let me know if there is anything else you may need. I apologize for no links or pictures, It says I cannot add those until I am an "verified user"

    Thanks,

    Ben Blandina




    Tuesday, August 13, 2019 12:17 AM
  • Any update based on my information? Can i please get help on this?
    Wednesday, October 9, 2019 6:50 PM