Use ADAL to Connect Your Universal Apps to Azure AD or ADFS
In short: using ADAL from a Universal App is easy, but not obvious.
For what we hear, your experience is that you try to add a reference to the ADAL NuGet in the shared project – and it fails.
There are a number of reasons for that. This post will give a bit of background, then will give you instructions on how to successfully use ADAL in a Universal App project. Hint: it does not entail adding ADAL to the shared project curious? read on!
Background
Universal Apps are a new app project type introduced in Visual Studio 2013 Update 2, which allows you to use a single solution to target Windows 8.1 and Windows Phone 8.1 and share a lot of code between the two, while still being able to tailor the experience to the features of every platform. Wow, I should not try to summarize too much in a single sentence if you want a less crumpled introduction to Universal Apps, head to this page. From now on I’ll just assume that you have some familiarity with the concept of Universal Apps, and go straight to my point.
Although the percentage of code that is common across the Tablet/PC and the phone platforms is astoundingly high, the WebAuthenticationBroker (WAB, main authentication API in the Windows Runtime and a key requirement for our own ADAL libraries) is one of the exceptions that confirms the rule. Back in April I wrote a post that went in the details of the calling pattern that the WAB requires on the phone, but it boils down to the fact the WAB acts a separate app – calling the WAB means leaving your app (which could be deactivated) and handling reactivation and parameters retrieval when the WAB is done. It’s the famous continuation model.
The continuation model has a pretty significant impact on the structure of the code that needs its services. As a result, we simply could not reuse the ADAL component we wrote for tablet/PC Windows Store apps – we had to create a brand new one for the phone that accommodated for the continuation model, as explored here.
Another reason that made it difficult to share the same component across tablet/pc and phone was the choice we made to package ADAL for Windows Store as a Windows Runtime Component. That gives you the ability of using any of the Windows Runtime languages (C#,WinJS, C++) when using ADAL in your app, but is pretty much antithetic to packaging in the portable class library (PCL) format that would have afforded us more sharing options. For future versions we are considering changing approach, but for this generation this is what we went for.
What to do, then? How do you use ADAL in a Universal App? Pretty easy, once you know the trick:
- You add a reference to the ADAL NuGet in BOTH the Windows 8.1 and Windows Phone 8.1 projects
- In the shared project you add all the classes & methods for handling the continuation methods, wrapped in #if WINDOWS_PHONE_APP / #endif directives
That’s all there is to it. I suspect there’s some cleverer sharing technique, and if some XAML expert chimes in with a proposal I am happy to revise the above; but in the meanwhile, the above works and is simple.
Use ADAL in a Universal App Project
Let’s make this a tad more practical. Fire up VS2013 (update 2 and above) and create a new blank Universal App.
The Windows 8.1 Part
Let’s start with adding some ADAL code to the Windows 8.1 part.
Right click on the Windows 8.1 project in solution explorer, choose Manage NuGet Packages, ensure that the first dropdown says “include prerelease”, type adal in the search box, and pick the entry for the library:
We are going to keep things real simple. Let’s add some basic logic in the app page to get a token for the Graph. I will not even use the token, I just want to demonstrate that ADAL works in this context.
Here there’s my super sophisticated page:
<Page x:Class="ADALUniversalApp1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ADALUniversalApp1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button HorizontalAlignment="Center" Click="Button_Click">aaa</Button> </Grid> </Page>
…and the code behind it:
using Microsoft.IdentityModel.Clients.ActiveDirectory; using System; using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace ADALUniversalApp1 { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { AuthenticationContext ac = new AuthenticationContext("https://login.windows.net/common"); AuthenticationResult ar = await ac.AcquireTokenAsync("https://graph.windows.net", "e11a0451-ac9d-4c89-afd8-d2fa3322ef68", new Uri("http://li")); MessageDialog mg = new MessageDialog("hello, Mr/Ms " + ar.UserInfo.FamilyName); await mg.ShowAsync(); } } }
All this does is requesting to a generic AAD tenant an access token scoped for the Graph, then showing one of the values found in the id_token that AAD sends down along with the access and refresh ones. I know, I knooow… no error checking. Lazy hippie.
This is code you can use directly with your own tenants without even modifying the values I pass in, given that I am using the common endpoint (see here) and the native clients are automagically available everywhere.
You do have to modify the values of authority/resource/clientid/returnURI if you want to go against ADFS – you’d need the values of what you provisioned in ADFS. However the code itself is the exact same. Apart from having to turn off authority validation, that is.
Let’s run this bad boy, shall we? Right click on the Windows 8.1 project, debug/start new instance. Click on the button.
Authenticate as any user from any tenant you like. If everything goes well, you’ll get the following:
The Windows 8.1 project in the Universal App solution works fine with ADAL. Check.
The Windows Phone 8.1 Part
Now the fun begins! No panic tho, this will be a slightly modified version of the project structure you can observe in this sample.
Stop the debugger, right click on the Windows Phone 8.1 project in solution explorer, choose Manage NuGet Packages, type adal in the search box, and pick the entry for the library:
Déjà vu? Wow, they must be changing something
That’s right, this is the exact same step you have done for the Windows 8.1 project, and the same ADAL NuGet package. However: the project type is different, hence the NuGet runtime will add in the project a different component – the one for Windows Phone 8.1.
The XAML source for the phone page is gloriously the same as the one I pasted earlier: same lonely button in the center of the screen.
The code behind, however, is far more colorful this time:
using Microsoft.IdentityModel.Clients.ActiveDirectory; using System; using Windows.ApplicationModel.Activation; using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace ADALUniversalApp1 { public sealed partial class MainPage : Page, IWebAuthenticationContinuable { AuthenticationContext ac = null; public MainPage() { this.InitializeComponent(); this.NavigationCacheMode = NavigationCacheMode.Required; } protected override async void OnNavigatedTo(NavigationEventArgs e) { ac = await AuthenticationContext.CreateAsync("https://login.windows.net/common"); } private async void Button_Click(object sender, RoutedEventArgs e) { AuthenticationResult result = await ac.AcquireTokenSilentAsync("https://graph.windows.net", "e11a0451-ac9d-4c89-afd8-d2fa3322ef68"); if (result != null && result.Status == AuthenticationStatus.Success) { ShowGreeting(result); } else { ac.AcquireTokenAndContinue("https://graph.windows.net", "e11a0451-ac9d-4c89-afd8-d2fa3322ef68", new Uri("http://li"), ShowGreeting); } } public async void ShowGreeting(AuthenticationResult ar) { MessageDialog mg = new MessageDialog("hello, Mr/Ms " + ar.UserInfo.FamilyName); await mg.ShowAsync(); } public async void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args) { await ac.ContinueAcquireTokenAsync(args); } } }
Well, there is decisively more stuff here. Allow me to briefly walk you through the various moving parts.
- The page implements IWebAuthenticationContinuable. That is an interface we’ll define later, but it is easily explained: it defines the ContinueWebAuthentication method, which is the very first thing that should be executed when the app is reactivated after a WAB authentication. If you jump to the bottom of the class definition, you’ll see the implementation of ContinueWebAuthentication: here it just passed back the results to ADAL (via ContinueAcquireTokenAsync) so that they can be used to conclude the token acquisition sequence.
- Here we define the AuthenticationContext at the page level, given that we need it in multiple places. We initialize it in OnNavigatedTo, using the factory that is typical of ADAL for Windows Phone (see this).
- The click event handler goes through the “try to get a token silently and do work – if it fails, acquire a token via continuation model and book the work to be executed once the token has been obtained” routine. Again, see this for details.
Note: I am using the exact same coordinates as the tablet/pc app. I don’t have to, but I do. - ShowGreeting is the function we want the app to perform. Here you’d put your real logic. Note that you can have as many as you need. See this sample for more details.
That takes care of the Windows Phone 8.1 project. However, if you would run this now nothing would work! We still need to hook up the logic that at reactivation time routes execution where we want it to be. That’s where things get tricky: we need to inject in the App (which lives in the shared project) a lot of logic that is specific to the phone platform.
First, let’s add to the shared project a class that encapsulates the details of the continuation model. You can take the corresponding class from our GitHub sample, you can find it here. Together with the continuation model, that class contains the definition of IWebAuthenticationContinuable.
If after adidng that class you’d try to compile, you’d get lots of errors. That’s because it uses many phone specific types. To solve the issue in a somewhat brisk fashion, simply add a #if WINDOWS_PHONE_APP on the line above the class definition and place a #endif at the very end of the file. That will take care of the compilation errors.
Next, open App.xaml.cs. Here we need to add the logic that will handle the OnActivated event and use the continuation manager class.
Search the first #if WINDOWS_PHONE_APP block, and declare in it a ContinuationManager:
#if WINDOWS_PHONE_APP private TransitionCollection transitions; ContinuationManager continuationManager; #endif
That done, scroll to the end of the class. Right below OnSuspending, insert the following block:
#if WINDOWS_PHONE_APP private Frame CreateRootFrame() { Frame rootFrame = Window.Current.Content as Frame; if (rootFrame == null) { rootFrame = new Frame(); rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; Window.Current.Content = rootFrame; } return rootFrame; } protected override async void OnActivated(IActivatedEventArgs e) { base.OnActivated(e); continuationManager = new ContinuationManager(); Frame rootFrame = CreateRootFrame(); if (rootFrame.Content == null) { rootFrame.Navigate(typeof(MainPage)); } var continuationEventArgs = e as IContinuationActivatedEventArgs; if (continuationEventArgs != null) { Frame scenarioFrame = Window.Current.Content as Frame; if (scenarioFrame != null) { continuationManager.Continue(continuationEventArgs, scenarioFrame); } } Window.Current.Activate(); } #endif
I could expand on what that code does, but for the goals of this post this could very well be the equivalent of Harry Potter weaving his wand toward the phone emulator and utter “Patronus Continuatio!” – which is a rather dorky way of saying “this is windows phone specific logic that impacts authentication only indirectly, hence see MSDN if you want more details”.
Let’s give the phone app a spin! Right click on the Windows Phone 8.1 project in solution explorer, right click/Debug/Start new instance.
Once the button appears, hit it. You’ll get the following:
That’s the WAB in its mobile attire. Sign in and…
Voila’. Token successfully acquired. Also the Windows Phone 8.1 portion of the Universal App works. Q.E.D.
Wrapup
Making ADAL work in a Universal App is not very intuitive, but is also not very difficult. The code required to get a token in the two platform does differ, which is somewhat un-Universal – however, modulo the need to add a NuGet reference twice, this is pretty much the same structural difference what you’d have to handle if you were to use the WebAuthenticationBroker directly instead of via ADAL. As mentioned here there are good reasons for the mobile WAB to adopt the continuation model – I think that some extra steps are well worth the extended reach to low powered devices that this model affords you.
I hope this post was useful to unblock you, if you have issues hit me via the Contact link in the top menu. Happy coding!