Desktop app that calls web APIs: Acquire a token interactively

The following example shows minimal code to get a token interactively for reading the user's profile with Microsoft Graph.

In MSAL.NET

string[] scopes = new string[] {"user.read"};
var app = PublicClientApplicationBuilder.Create(clientId).Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result;
try
{
 result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
             .ExecuteAsync();
}
catch(MsalUiRequiredException)
{
 result = await app.AcquireTokenInteractive(scopes)
             .ExecuteAsync();
}

Mandatory parameters

AcquireTokenInteractive has only one mandatory parameter, scopes, which contains an enumeration of strings that define the scopes for which a token is required. If the token is for Microsoft Graph, the required scopes can be found in the API reference of each Microsoft Graph API in the section named "Permissions." For instance, to list the user's contacts, the scope "User.Read", "Contacts.Read" must be used. For more information, see Microsoft Graph permissions reference.

On Android, you also need to specify the parent activity by using .WithParentActivityOrWindow, as shown, so that the token gets back to that parent activity after the interaction. If you don't specify it, an exception is thrown when calling .ExecuteAsync().

Specific optional parameters in MSAL.NET

WithParentActivityOrWindow

The UI is important because it's interactive. AcquireTokenInteractive has one specific optional parameter that can specify, for platforms that support it, the parent UI. When used in a desktop application, .WithParentActivityOrWindow has a different type, which depends on the platform. Alternatively you can omit the optional parent window parameter to create a window, if you do not want to control where the sign-in dialog appears on the screen. This would be applicable for applications which are command line based, used to pass calls to any other backend service and do not need any windows for user interaction.

// net45
WithParentActivityOrWindow(IntPtr windowPtr)
WithParentActivityOrWindow(IWin32Window window)

// Mac
WithParentActivityOrWindow(NSWindow window)

// .NET Standard (this will be on all platforms at runtime, but only on NetStandard at build time)
WithParentActivityOrWindow(object parent).

Remarks:

  • On .NET Standard, the expected object is Activity on Android, UIViewController on iOS, NSWindow on Mac, and IWin32Window or IntPr on Windows.

  • On Windows, you must call AcquireTokenInteractive from the UI thread so that the embedded browser gets the appropriate UI synchronization context. Not calling from the UI thread might cause messages to not pump properly and deadlock scenarios with the UI. One way of calling Microsoft Authentication Libraries (MSALs) from the UI thread if you aren't on the UI thread already is to use the Dispatcher on WPF.

  • If you're using WPF, to get a window from a WPF control, you can use the WindowInteropHelper.Handle class. Then the call is from a WPF control (this):

    result = await app.AcquireTokenInteractive(scopes)
                      .WithParentActivityOrWindow(new WindowInteropHelper(this).Handle)
                      .ExecuteAsync();
    

WithPrompt

WithPrompt() is used to control the interactivity with the user by specifying a prompt. The exact behavior can be controlled by using the Microsoft.Identity.Client.Prompt structure.

The struct defines the following constants:

  • SelectAccount forces the STS to present the account selection dialog box that contains accounts for which the user has a session. This option is useful when application developers want to let users choose among different identities. This option drives MSAL to send prompt=select_account to the identity provider. This option is the default. It does a good job of providing the best possible experience based on the available information, such as account and presence of a session for the user. Don't change it unless you have good reason to do it.
  • Consent enables the application developer to force the user to be prompted for consent, even if consent was granted before. In this case, MSAL sends prompt=consent to the identity provider. This option can be used in some security-focused applications where the organization governance demands that the user is presented with the consent dialog box each time the application is used.
  • ForceLogin enables the application developer to have the user prompted for credentials by the service, even if this user prompt might not be needed. This option can be useful to let the user sign in again if acquiring a token fails. In this case, MSAL sends prompt=login to the identity provider. Sometimes it's used in security-focused applications where the organization governance demands that the user re-signs in each time they access specific parts of an application.
  • Create triggers a sign-up experience, which is used for External Identities, by sending prompt=create to the identity provider. This prompt should not be sent for Azure AD B2C apps. For more information, see Add a self-service sign-up user flow to an app.
  • Never (for .NET 4.5 and WinRT only) won't prompt the user, but instead tries to use the cookie stored in the hidden embedded web view. For more information, see web views in MSAL.NET. Using this option might fail. In that case, AcquireTokenInteractive throws an exception to notify that a UI interaction is needed. You'll need to use another Prompt parameter.
  • NoPrompt won't send any prompt to the identity provider which therefore will decide to present the best sign-in experience to the user (single-sign-on, or select account). This option is also mandatory for Azure Active Directory (Azure AD) B2C edit profile policies. For more information, see Azure AD B2C specifics.

WithUseEmbeddedWebView

This method enables you to specify if you want to force the usage of an embedded WebView or the system WebView (when available). For more information, see Usage of web browsers.

var result = await app.AcquireTokenInteractive(scopes)
                    .WithUseEmbeddedWebView(true)
                    .ExecuteAsync();

WithExtraScopeToConsent

This modifier is used in an advanced scenario where you want the user to pre-consent to several resources upfront, and you don't want to use incremental consent, which is normally used with MSAL.NET/the Microsoft identity platform. For more information, see Have the user consent upfront for several resources.

var result = await app.AcquireTokenInteractive(scopesForCustomerApi)
                     .WithExtraScopeToConsent(scopesForVendorApi)
                     .ExecuteAsync();

WithCustomWebUi

A web UI is a mechanism to invoke a browser. This mechanism can be a dedicated UI WebBrowser control or a way to delegate opening the browser. MSAL provides web UI implementations for most platforms, but there are cases where you might want to host the browser yourself:

  • Platforms that aren't explicitly covered by MSAL, for example, Blazor, Unity, and Mono on desktops.
  • You want to UI test your application and use an automated browser that can be used with Selenium.
  • The browser and the app that run MSAL are in separate processes.
At a glance

To achieve this, you give to MSAL start Url, which needs to be displayed in a browser of choice so that the end user can enter items such as their username. After authentication finishes, your app needs to pass back to MSAL end Url, which contains a code provided by Azure AD. The host of end Url is always redirectUri. To intercept end Url, do one of the following things:

  • Monitor browser redirects until redirect Url is hit.
  • Have the browser redirect to a URL, which you monitor.
WithCustomWebUi is an extensibility point

WithCustomWebUi is an extensibility point that you can use to provide your own UI in public client applications. You can also let the user go through the /Authorize endpoint of the identity provider and let them sign in and consent. MSAL.NET can then redeem the authentication code and get a token. For example, it's used in Visual Studio to have electrons applications (for instance, Visual Studio Feedback) provide the web interaction, but leave it to MSAL.NET to do most of the work. You can also use it if you want to provide UI automation. In public client applications, MSAL.NET uses the Proof Key for Code Exchange (PKCE) standard to ensure that security is respected. Only MSAL.NET can redeem the code. For more information, see RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients.

using Microsoft.Identity.Client.Extensions;
Use WithCustomWebUi

To use .WithCustomWebUI, follow these steps.

  1. Implement the ICustomWebUi interface. For more information, see this website. Implement one AcquireAuthorizationCodeAsyncmethod and accept the authorization code URL computed by MSAL.NET. Then let the user go through the interaction with the identity provider and return back the URL by which the identity provider would have called your implementation back along with the authorization code. If you have issues, your implementation should throw a MsalExtensionException exception to nicely cooperate with MSAL.

  2. In your AcquireTokenInteractive call, use the .WithCustomUI() modifier passing the instance of your custom web UI.

    result = await app.AcquireTokenInteractive(scopes)
                      .WithCustomWebUi(yourCustomWebUI)
                      .ExecuteAsync();
    
Examples of implementation of ICustomWebUi in test automation: SeleniumWebUI

The MSAL.NET team has rewritten the UI tests to use this extensibility mechanism. If you're interested, look at the SeleniumWebUI class in the MSAL.NET source code.

Provide a great experience with SystemWebViewOptions

From MSAL.NET 4.1 SystemWebViewOptions, you can specify:

  • The URI to go to (BrowserRedirectError) or the HTML fragment to display (HtmlMessageError) in case of sign-in or consent errors in the system web browser.
  • The URI to go to (BrowserRedirectSuccess) or the HTML fragment to display (HtmlMessageSuccess) in case of successful sign-in or consent.
  • The action to run to start the system browser. You can provide your own implementation by setting the OpenBrowserAsync delegate. The class also provides a default implementation for two browsers: OpenWithEdgeBrowserAsync and OpenWithChromeEdgeBrowserAsync for Microsoft Edge and Microsoft Edge on Chromium, respectively.

To use this structure, write something like the following example:

IPublicClientApplication app;
...

options = new SystemWebViewOptions
{
 HtmlMessageError = "<b>Sign-in failed. You can close this tab ...</b>",
 BrowserRedirectSuccess = "https://contoso.com/help-for-my-awesome-commandline-tool.html"
};

var result = app.AcquireTokenInteractive(scopes)
                .WithEmbeddedWebView(false)       // The default in .NET Core
                .WithSystemWebViewOptions(options)
                .Build();

Other optional parameters

To learn more about all the other optional parameters for AcquireTokenInteractive, see AcquireTokenInteractiveParameterBuilder.

Next steps

Move on to the next article in this scenario, Call a web API from the desktop app.