Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

Acquiring tokens interactively Public client application flows

Bogdan Gavril edited this page Apr 23, 2019 · 32 revisions

ADAL.NET applications can acquire a Token by calling AcquireTokenAsync. There are several overrides of this method:

  • They don't take the same parameters to express what is (or are) the Web API(s) for which the token is requested. ADAL is about the Azure AD v1.0 endpoint, and therefore gets tokens for a resource
  • They have ways of controlling what UI will be presented to the user.
  • They are .NET async methods, which return a Task

The code snippet below shows a simple acquisition of a token

  var ac = new AuthenticationContext("https://login.microsoftonline.com/<tenantId>");
  AuthenticationResult result;
  result = await ac.AcquireTokenAsync("<clientId>",
                                      "https://resourceUrl",
                                      new Uri("https://ClientReplyUrl"),
                                      new PlatformParameters(PromptBehavior.Auto));

Interactive overrides of AcquireTokenAsync in ADAL.NET

Acquiring a token interactively in ADAL.NET is done by calling certain overrides of AcquireTokenAsync (the ones starting with the resource and then ClientId).

There is a small difficulty in ADAL.NET: there are other overrides of AcquireTokenAsync which are not interactive, but rather confidential client flows (such as the on-behalf-of flow used for Web APIs to call another Web API in the name of the user). The non-interactive overrides of AcquireTokenAsync are masked in the picture below to really highlight this important information

image

Overrides of AcquireTokenAsync

Acquiring a token interactively with ADAL.NET requires to pass:

  • The resource for which you want an access token. Here you can pass either the Resource URI of a Web API, or the clientId of the target Web API. Both work, but it's important to realize that the token will contain the resource as requested (audience), and therefore the form to use is the one accepted by the Web API.
  • The clientId parameter is the clientId/applicationId of the public client application.
  • The redirectUri is the redirect Uri of the public client application. This is the address to return upon receiving a response from the STS. Note that Windows desktop applications this can be anything (for example http://MyApp). For Android, iOS and UWP applications, this has a special format so that it's unique and the broker (Company Portal, Microsoft Authenticator) can call the application back.
  • parameters of type PlatformParameters is the ADAL.NET way of expressing what kind of interaction needs to happen with the user. Note that PlatformParameters is different depending on the platforms (See below).
  • userId is optional. It's set from the DisplayableId of a user. It's a hint helping the STS pre-populate the username field in the authentication form. This does not prevent the end user to edit the username field and authenticate as a different user, though. The default is UserIdentifier.Any.
  • extraQueryParameters (optional) enables application developers to provide extra parameters to the STS endpoints. This can be hints, or a kind of extension point for parameters not exposed directly through the API. This is a comma separated string of keys/values separated themselves by an ampersand: "key1=value1&key2=value2".
  • Note that ADAL.NET also checks if a specific environment variable exists (ExtraQueryParameter) and if it does it adds additional query parameter to each query to the STS endpoint.
  • Claims (optional) is used in the very specific scenario of conditional access.
    • The idea is that a previous call was successfully made to AcquireTokenAsync, and the access token from the AuthenticationResult was used to access a Web API, but this Web API realized that it needed more claims about the user, that is more evidence of his/her identity (for instance it needed the user to do two factor authentication). At that moment, ADAL.NET throws an AdalClaimChallengeException, which contains a Claims property. The client application must then call again AcquireTokenAsync passing as a Claim parameter the value of the Claims property from the AdalClaimChallengeException.
    • This scenario is explained in details in the Conditional Access article.
    • There is also a sample in github illustrating it for ADAL.NET: https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof-ca
    • Note that on Windows, you must call AcquireTokenAsync for interactive flows on the UI thread so that the embedded browser gets the appropriate UI synchronization context. Not calling from the UI thread may cause messages to not pump properly and/or deadlock scenarios with the UI.

All overrides but one try to get token from the cache first

Most overrides of AcquireTokenAsync, will try to get a token from the cache before presenting the interactive UI. There is one notable exception: the override of AcquireTokenAsync taking a claim parameter. It does not even try to hit the cache, it's always interactive because the cached token does not have the right claims (otherwise an AdalClaimChallengeException would not have been thrown).

Controlling the interactivity with the user with PlatformParameters

The interactive overrides of AcquireTokenAsync take an instance of PlatformParameter. This is a class which is different depending on the platforms, it's used to control the interaction of ADAL.NET with the user, and also, in the platform supporting it with the broker.

Properties (or PlatformParameter's constructors parameters) common to most platforms

Except for .NET Core, which does not provide any user interaction, the parameters are the following:

  • PromptBehavior: It's an enumeration. It's the only parameter which is common to all the platforms, even if not all the constants are defined in all the platforms. Its values are:
    • Auto: the user is prompted for credentials only when needed.

      • If a token already exists in the cache, no dialog is presented at all.
      • If no token exists in the cache, but the user is known, or a session cookie is available in the web browser control used for the interaction, the dialog flashes but disappears immediately.
      • If no token exists in the cache and the user is not known, the dialog is presented for the user to log-in
    • Always: the user is always prompted for credentials, even if a token exists in the cache, and if a user has a session. This is useful when the application wants to give the user an opportunity to sign-in with a different identity without giving any hint of previous identities. Note that the sign-in dialog is presented with the email and phone field completely blank image

    • RefreshSession: the web browser is displayed, and the token gets updated claims. If the logon cookies are available (in the embedded web view) the user won't be prompted for credentials, and the dialog will quickly disappear (there will be a flash).

    • SelectAccount: this shows a dialog containing the identities under which the user has currently sessions. The user can add other accounts. image

    • Never: [except on Android and iOS]: does not prompt the user for credentials.

  • CallerActivity [Android] or OwnerWindows [.NET 4.5] enable application developers to control what window/activity will be the parent of the authentication dialog. A developer might want to provide this parent information for UX reasons: to ensure that the dialog to appear centered on the parent window and over it.

Note that:

  • If you use PromptBehavior = Never and the token does not exist in the cache, or needs to be refreshed with UI, the call to AcquireTokenAsync will fail with an exception of type AdalException, with the following message:

    user_interaction_required: One of two conditions was encountered: 
    1. The PromptBehavior.Never flag was passed, but the constraint could not be honored, because user interaction was required. 
    2. An error occurred during a silent web authentication that prevented the http authentication flow from completing in a  short enough time frame
    
  • The following table summarizes the prompt values which are sent to AAD depending on the specified PromptBehavior. This information can be useful if you need to use a tool like Fiddler to debug your application and understand what happens:

    Prompt behavior Value of the prompt= Query parameter
    Auto ADAL does a best effort, and therefore, if the token exists in the cache, no prompt will be sent to AAD. If a token needs to be refreshed, this will happen, and if the user needs to be signed-in, the prompt=login will be sent.
    Always prompt=login
    RefreshSession Prompt=refresh_session
    SelectAccount Prompt=select_account
    Never Prompt=attempt_none. The behavior is the same as prompt=none for managed users (in AAD). However, for federated users, AAD redirects to ADFS as it cannot determine in advance whether ADFS can login the user silently (i.e. via WIA) or not.

Property specific to mobile platforms relative to the brokers

On Android and iOS,UseBroker specifies if the authentication should be delegated to a broker. On Android, users can install Microsoft Authenticator or Company portal which are brokers. If they do, they can benefit from device id and SSO between applications and the web applications. On iOS or Windows Phone, they can install Microsoft Authenticator. On Windows there is another broker which is not yet supported by our authentication libraries: WAM (Web Authentication Manager). Note that on iOS and Android, when requiring a broker, you also need to handle the response from the broker:

On Android

On Android, you need to override the OnActivityResult method of the Activity and call the SetAuthenticationAgentContinuationEventArgs method of the AuthenticationAgentContinuationHelper ADAL class.

protected override void OnActivityResult(int requestCode, 
                                             Result resultCode, Intent data)
{
 base.OnActivityResult(requestCode, resultCode, data);
 AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode,
                                                                                   resultCode,
                                                                                   data);
}

On iOS

On iOS, you need to override the OpenUrl method of the FormsApplicationDelegate derived class, in the same way (but with different parameters)

protected override void OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
 AuthenticationContinuationHelper.SetBrokerContinuationEventArgs(url);
 return true;
}

you will also need to define a URL scheme, require permissions for your app to call another app, have a specific form for the redirect URL, and register this redirect URL in the Azure portal. This is explained in detail in Leveraging brokers on Android and iOS

Properties of PlatformParameter specific to iOS to fine grain control the UX

On iOS, PlatformParameter has other members enabling application developer to control iOS specific UX visuals or transitions. The members might have different authorized values depending on the targeted iOS platform (iPhone, iPad, TV, watch):

  • PreferredStatusBarStyle (of type UIKit.UIStatusBarStyle) enables the application developer to set the preferred status bar style for the login form (default, BlackTransucent, LightContent, BlackOpaque). This enumeration is not available on TvOS and watchOS
  • ModalTransitionStyle (of type UIKit.UIModalTransitionStyle) enables the application developer to set the transition style used when the login view is presented among (CoverVertical, FlipHorizontal, CrossDissolve, PartialCurl). This is not available on watchOS
  • ModalPresentationStyle (of type UIKit.UIModalPresentationStyle) enables the application developer to set the presentation style used when the login form view is presented (FullScreen, PageSheet, FormSheet, CurrentContext, Custom, OverFullScreen, OverCurrentContext, Popover, BlurOverFullScreen)
  • TransitioningDelegate (of type UIKit.UIModalPresentationStyle) enables application developers to fine tune the transitions by providing custom code.

Properties of PlatformParameter specific to WinRT and UWP (Corporate network)

The WinRT (until ADAL 3.x) and UWP platforms have the following property UseCorporateNetwork is a boolean which enables the Win8.1 and UWP application to benefit from windows integrated authentication (and therefore SSO with the user signed-in with the operating system) if this user is signed-in with an account in a federated Azure AD tenant. This leverages WAB (Web Authentication Broker).

Important: Setting this property to true assumes that the application developer has enabled Windows Integrated Authentication (WIA) in the application. For this:

  • In the Package.appxmanifest for your UWP application, in the Capabilities tab, enable the following capabilities:
    • Enterprise Authentication
    • Private Networks (Client & Server)
    • Shared User Certificate WIA is not enabled by default because applications requesting the Enterprise Authentication or Shared User Certificates capabilities require a higher level of verification to be accepted into the Windows Store, and not all developers may wish to perform the higher level of verification.

Note that the underlying implementation on the UWP platform (WAB) does not work correctly in Enterprise scenarios where Conditional Access was enabled. The symptom is that the user tries to sign-in with Windows hello, and is proposed to choose a certificate, but the certificate for the pin is not found, or the user chooses it, but never get prompted for the Pin. A workaround is to use an alternative method (username/password + phone authentication), but the experience is not good. In the future, ADAL and MSAL will need to leverage WAM, which will solve the problem.

Getting the Redirect URI in the case of Windows Universal Apps

Note: Support for Win8.1 and Windows Phone 8.1 stopped in ADAL 4.x. Windows 10 application (UWP) are still supported

In the case of windows store applications, you will need to discover the callback uri for your Windows Phone app. The simplest way to do it is to add a line in the Initialization method (for instance in the MainPage) and set a breakpoint on this line in the method:

var redirectURI = Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri();

then, run the app, and copy aside the value of redirectUri when the breakpoint is hit. It should look something like ms-app://s-1-15-2-1352796503-54529114-405753024-3540103335-3203256200-511895534-1429095407/ Back on the ReplyURLs tab of your application in the Azure portal, add this value. See for instance the nativeclient-windowsstore sample

Troubleshooting and known issues

In some cases, users have reported that the authentication in a UWP application does not work when they are in their corporate network. This seems to be due to a configuration of WEB. For more details see issue 1032: Can't login when inside corporation network. The workaround is to enable WAB to process private networks by the following shell command (for each user):

REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\authhost.exe" /v EnablePrivateNetwork /t REG_DWORD /d 1 /f

On the UWP simulator, the browser pop-up does not seem to remember the users that were logged in earlier. This does not occur when debugging on a local Windows machine.

Samples illustrating acquiring tokens interactively with ADAL.NET

Sample Platform Description
active-directory-dotnet-native-desktop Desktop (WPF) -> ASP.NET A .NET 4.5 WPF application that authenticates a user and calls web API using Azure AD and OAuth 2.0 access tokens.
active-directory-dotnet-webapi-manual-jwt-validation Desktop (WPF) -> ASP.NET Variation of the previous sample, this samples shows how to manually process the JWT access token in a web API using the JSON Web Token Handler For the Microsoft .Net Framework 4.5. It does not bring any new knowledge, in term of usage of ADAL.NET, and focuses on the API protection. It's mentioned here for completeness.
active-directory-dotnet-native-multitarget Xamarin (Android, iOS, UWP) A Xamarin application that signs users in with Azure Active Directory across several different platforms and calls the Microsoft Graph API using OAuth 2.0 access tokens
active-directory-dotnet-native-aspnetcore Desktop (WPF) -> ASP.NET Core 2.0 A WPF application that calls a Web API running on ASP.NET Core 2.0 protected by Azure AD using OAuth 2.0 access tokens.
Clone this wiki locally