Skip to content

Username Password Authentication

Jean-Marc Prieur edited this page Oct 16, 2018 · 40 revisions

Username / Password flow

In your desktop application you can use the Username/Password flow to acquire a token silently. No UI is required when using the application.

This flow is not recommended because your application asking users for their password is not secure. See this article for more details. The preferred flow for acquiring a token silently on Windows domain joined machines is Integrated Windows Authentication.

Constraints

Apart from the Integrated Windows Authentication constraints, the following constraints also apply:

  • Available starting with MSAL 2.1.0
  • The Username/Password flow is not compatible with conditional access and multi-factor authentication. This means that if your app runs in an Azure AD tenant where the tenant admin requires multi-factor authentication, you cannot use this flow (many organization do that)
  • It works only for Work and school accounts (not MSA)
  • The flow is available on .net desktop and .net core, but not on UWP

How to use it?

PublicClientApplicationcontains the method AcquireTokenByUsernamePasswordAsync

You can use an empty username in which case the library will try to query Windows for the AD or AAD joined user. However depending on the way your Windows administrator has setup the policies, it can be possible that applications on your windows machine are not allowed to lookup the logged-in user.

The following sample presents the most current case, with explanations of the kind of exceptions you can get, and their mitigations

static async Task GetATokenForGraph()
{
    string authority = "https://login.microsoftonline.com/contoso.com";
    string[] scopes = new string[] { "user.read" };
    PublicClientApplication app = new PublicClientApplication(clientId, authority);
    var accounts = await app.GetAccountsAsync();

    AuthenticationResult result = null;
    if (accounts.Any())
    {
        result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
    }
    else
    {
        try
        {
            var securePassword = new SecureString();
            foreach (char c in "dummy") // you should fetch the password keystroke by keystroke
                securePassword.AppendChar(c);

            result = await app.AcquireTokenByUsernamePasswordAsync(scopes, "[email protected]", securePassword);
        }
        catch (MsalUiRequiredException ex)
        {
            // MsalUiRequiredException: AADSTS65001: The user or administrator has not consented to use the application 
            // with ID '{appId}' named '{appName}'.Send an interactive authorization request for this user and resource.

            // Explanation: you need to get user consent for the scopes first.
            // Mitigation:  This can be done, if you are not using .NET Core (which does not have any Web UI)
            // by doing (once only) an AcquireToken interactive.
            // If you are don't want to do an AcquireTokenInteractive, you might want to suggest the user to navigate
            // to a URL to consent: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read
            // Alternatively, configure your application in the AAD portal to not require consent for the respective scope


            // MsalUiRequiredException AADSTS50079: The user is required to use multi-factor authentication.

            // Explanation: as detailed in the constraints section, U/P auth flow does not deal with conditional access
            // Mitigation: use Integrated Windows Authentication if the app runs on domain joined / AAD joined machines
            // or Interactive authentication on .NET framework, or Device Code flow otherwise
        }
        catch (MsalServiceException ex)
        {
            // Kind of errors you could have (in ex.Message)

            // MsalServiceException: AADSTS90010: The grant type is not supported over the /common or /consumers endpoints. Please use the /organizations or tenant-specific endpoint.
            // you used common.
            // Mitigation: as explained in the message from Azure AD, the authoriy needs to be tenanted or otherwise organizations

            // MsalServiceException: AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'.
            // Explanation: this can happen if your application was not registered as a public client application in Azure AD 
            // Mitigation: in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true` 

            // MsalServiceException: AADSTS50079: The user is required to use multi-factor authentication.

            // Explanation: as explained in the constraints section, U/P auth flow does not deal with conditional access
            // Mitigation: use Integrated Windows Authentication or revert to ADAL

        }
        catch (MsalClientException ex)
        {
            // Error Code: unknown_user Message: Could not identify logged in user
            // Explanation: You passed in an empty username and the library was unable to query the current Windows logged-in user or this user is not AD or AAD 
            // joined (work-place joined users are not supported). 

            // Mitigation 1: on UWP, check that the application has the following capabilities: Enterprise Authentication, 
            // Private Networks (Client and Server), User Account Information

            // Mitigation 2: Implement your own logic to fetch the username (e.g. [email protected]) and use the 
            // AcquireTokenByIntegratedWindowsAuthAsync overload that takes in the username
        }
    }

    Console.WriteLine(result.Account.Username);
}

Getting started with MSAL.NET

Acquiring tokens

Desktop/Mobile apps

Web Apps / Web APIs / daemon apps

Advanced topics

News

FAQ

Other resources

Clone this wiki locally