ADAL .NET Servicing Release and the Never Flag

The team just published an update to ADAL .NET: you can find the new package in ADAL’s home on NuGet here as v1.0.3.

This is largely a service update – most of the work that went on it is bug fixing: we adjusted some weird cache behavior, we improved ADAL’s behavior in error-prone situations such as temporary lack of connectivity, and so on.

We did add one new feature, though. We extended the PromptBehavior enum to include a new value, Never. Let’s dig into that for a bit.

You might recall from earlier posts that ADAL’s AcquireToken has an overload accepting a parameter of type PromptBehavior. Until v1.0.2, PromptBehavior featured the following two values:

  • Auto: the default. In this mode, ADAL will do everything in its power to return a token complying to the requirements passed in the AcquireToken call (issued for that specific clientID, scoped for the requested resource, etc) without showing a UI: that boils down to looking up the cache for a corresponding cached token, failing that looking up for a suitable (MRRT) refresh token and using it. If that fails to retrieve a token, ADAL falls back to popping out a dialog hosting a browser and navigating to the authorization endpoint of the authority of choice (Windows Azure AD, ADFS).
    This is the typical behavior you want when you trust the library to minimize user prompts but you can tolerate such prompts to pop out when needed.
  • Always: this flag tells ADAL to disregard the cache and refresh token lookups, and directly prompt the user regardless of the cached state.
    You’ll want to do this when you want to guarantee that the user is given an opportunity of specifying a different account from the ones that might already have been used to obtain the tokens already present in cache. Say that you are developing an application which shows side by side resources from different tenants: you want to be able to obtain tokens for multiple, different users and keep them all in the cache at once. Say that you already got a token for resource R with user A: without something like the Always flag, a 2nd call to AcquireToken for R would always automatically pick up the token you obtained with A, preventing the user from ever being able to specify B.

    Those two values are fine and dandy, but in fact they don’t cover all possible behaviors you might want to get from AcquireToken. Say that you are doing some background work, like refreshing a secondary view, and that work requires you to access protected resources. Your user is focused on the primary view and does not even know that you are doing work on the background. Say that the token acquisition for some of the resources you are accessing in the background fails to take place via cache: would you interfere with the main view and interrupt the user’s work with a credential prompt out of the blue? The user would not even understand why he/she is being prompted!

    That is why we are introducing a new value in PromptBehavior:

  • Never: this flag guarantees that no UI will be displayed in the context of the present AcquireToken call.
    It tells ADAL to do whatever it is in its power to obtain the requested token without showing any UI, just like for Auto, but if that fails it should stop and return an error that you can trap and handle as you deem appropriate for your application. For example, just to give you an idea: if the resource you were trying to access has a visual representation in your UI you could add a little warning icon or any other signal which gently informs the user about the current state and provide an affordance to explicitly initiate an interactive authentication flow when he/she sees fit.

    If the story would stop here, I’d say it would be already interesting: but it gets better Smile to explain how, I have to peel off an abstraction layer and tell you a bit about the inner workings of the authentication flows and the .NET platform.

    When you go through a successful interactive AcquireToken flow, the state of your app is altered in two aspects:

  • ADAL’s token cache gets a new entry: access token, refresh token, occasionally user info, and so on. if you are working with Windows Azure AD, the refresh token is actually a TGT-like artifact which will allow you to silently obtain other access tokens for other resources within the same tenant.
  • As the interaction entails popping out a browser and authenticating to one or more web sites (the IdPs that serve pages for authenticating you: Windows Azure AD, MSA if you are using a guest account, etc) the web flow results in one or more session cookies that are now saved in your process’ cookie jar, where they will stay as long as your process is up or you explicitly nuke them (via some DLL imports).

    Now comes the good part. The effect of the Never flag is not to suppress all browser activity in case the cache & refresh token lookup fails: rather, it is to use an invisible browser to hit the authorization endpoint. Aha, I can almost see a light in your eyes! Smile This has two implications:

  • If you already have a session cookie in my process jar for the IdP you want, for example thanks to a preceding interaction, the Never flag will allow you to get a token with no UI interaction even if you don’t have any suitable refresh tokens in the cache
  • For guest scenarios, like the ones in which you use an MSA for accessing Windows Azure management APIs, use of Never + the presence of an MSA cookie allows you to silently obtain tokens cross-tenant – whereas MRRTs are bounded to work within a single tenant.

    image

    Phase 1: get a token via usual interactive flow

    Phase 2: delete the token cache content for any reason

    image

    Phase 3: profit! Ehmm, I mean – using the Never flag the cookie from the process jar is picked up and used to authenticate silently

     

  • That is pretty neat! But it is also very abstract. How about a quick demonstration of #1? Consider the following code.

       1:  AuthenticationContext ac = 
       2:       new AuthenticationContext("https://login.windows.net/contoso7.onmicrosoft.com");
       3:   
       4:  //the first call shows the UI
       5:  AuthenticationResult ar = ac.AcquireToken("https://contoso7.onmicrosoft.com/RichAPI",
       6:                      "be182811-9d0b-45b2-9ffa-52ede2a12230",
       7:                      new Uri("http://whatevah"));
       8:   
       9:  //the 2nd call hits the cache
      10:  ar = ac.AcquireToken("https://contoso7.onmicrosoft.com/RichAPI",
      11:                      "be182811-9d0b-45b2-9ffa-52ede2a12230",
      12:                      new Uri("http://whatevah"));
      13:   
      14:  //flush the cache
      15:  ac.TokenCacheStore.Clear();
      16:   
      17:  //the cookie jar already has the right cookie - the browser briefly flashes in and out of view
      18:  ar = ac.AcquireToken("https://contoso7.onmicrosoft.com/RichAPI",
      19:                       "be182811-9d0b-45b2-9ffa-52ede2a12230",
      20:                       new Uri("http://whatevah"));
      21:  //flush the cache again
      22:  ac.TokenCacheStore.Clear();
      23:   
      24:  //this call is like the 3rd call, but the Never flag makes it fully silent
      25:  ar = ac.AcquireToken("https://contoso7.onmicrosoft.com/RichAPI",
      26:                      "be182811-9d0b-45b2-9ffa-52ede2a12230",
      27:                      new Uri("http://whatevah"),
      28:                      PromptBehavior.Never);

    Let’s walk through what happens. Assume we are running this right when the process starts.

    When line 5 executes, you get the full interactive experience: there is not suitable token in cache yet, not passing any PromptBehavior means defaulting to Auto, hence this has to result in a prompt.

    image

    (you might notice that the title of the dialog is a bit weird. We have a service side bug these days, hopefully that will be gone soon but it does not interfere with actual auth).

    After successful authentication, AcquireToken returns and you get back a nice AuthenticationResult. Also, the cache has now one new entry:

    image

    Let’s make the exact same call, as in line 10. We have just the right token in the cache, and we get it back right away.

    Now, let’s play a bit. Line 15 flushes the cache, so that we end up with zero tokens.

    image

    Let’s run line 18, which contains the exact same call.

    It’s hard to show without posting a video, but if you try that code yourself you’ll see it very clearly: the browser briefly appears, but disappears right away before you have a chance of seeing anything. That’s because your process jar already has a session cookie for the authority, which gets posted as soon as you hit its authorization endpoint, hence you find yourself automatically authenticated and AcquireToken returns your token right away.

    Now, let’s repeat the token cache flushing (line 22) and let’s repeat the call, this time with the Never flag: you’ll get back a token, nice and clean, without any prior refresh token in the cache and without any confusing browser flashing.

    Well, that’s pretty much it for this servicing release!
    TL;DR: more power for you. Let us know if you find the new flag useful Smile

  • Leave a Reply

    Your email address will not be published. Required fields are marked *