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:
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.
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:
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 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:
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! This has two implications:
Phase 1: get a token via usual interactive flow
Phase 2: delete the token cache content for any reason
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.
(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:
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.
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