Fun With Windows Azure AD: Calling REST Services from a Windows Phone 8 App
[In short: I put together a quick & dirty sample which shows how you can get a Windows Azure AD token from a Windows Phone 8 application. Total hack, not official guidance, this is my personal blog, yadda yadda yadda.]
Last week we released the preview of some new interesting Windows Azure AD features, a code grant authorization endpoint and a version of AAL targeted at Windows Store applications.
In the last few days may of you guys asked me if AAL for Windows Store could also be used for Windows Phone 8 applications: and who can blame you, given the latest news?
As described in the deep dive post, AAL for Windows Store really takes advantage of many of the unique features of the Windows Runtime: a number of those features are not available on the phone, hence AAL for Windows Store will only work on Windows 8 and Windows RT devices.
That said: does that mean that you cannot take advantage of the great features of Windows Azure AD from your shiny Windows Phone 8 device? Nothing further from the truth.
A library saves you from the hassle of having to deal with protocol and token lifecycle in every app you write, but if you are willing to get your hands a bit dirty you can definitely get something going from scratch
Here’s what I did. Since my wife insisted to watch “Once Upon a Time”, yesterday night I resolved to put together a quick a dirty PoC to show how easy it is to get an access token from a Windows Phone 8 app directly from the Windows Azure endpoint(s). Admittedly using horrible shortcuts (app-global properties for communicating across pages? really?) but I got the thing done in just about 1 hour and 30 mins, which I believe proves my point (especially given that I haven’t touched Windows Phone development tools since this guy and his brother). Tonight I am writing down how it went. Enjoy!
The General Idea
The first principle is that I wanted to do as little as possible. I used as starting point the AAL for Windows Store sample here: I reused the exact same Web API project as my backend (though I had to do a small trick for making it accessible form the emulator), and I used the Windows Store client to create todo entries so that I did not have to implement write operations in my WP8 client. Also: I probably should not mention this, because it is REALLY bad practice, but I did not even bother to create a new serviceprincipal and permission entries for the WP8 client: I reused the settings already in place. Professional driver on a closed course, do not attempt! Always register separate entries for your clients or you’ll end up with an unmanageable mess.
About the WP8 client itself: I decided to start with an empty WP8 app, and have the bare minimum:
- a home page, with buttons for triggering the “sign in” and for calling a read operation of the Web API backend
- a page for taking care of the “sign in” flow
The code in this project is purposefully not very reusable. If this post serves a purpose, it should be to help you to grok how the protocol flow works: reusing this quick & dirty code “as is” would not be a very good idea, given that working at the protocol level means you are now taking on the responsibility (normally on the library) of implementing a flow that is sound and secure.
And now, without further ado…
The Windows Phone 8 App Client
I started with an empty Windows Phone application, which I added as a new project to the existing solution and added it right away in the multi-start debug settings.
Global Settings
Now, what to do next? Let’s think for a moment. I want the app to be able to obtain an access token from Windows Azure AD. From the work with AAL, we know that this entails providing some key coordinated describing the client itself (client ID, return URI), the resource I want to access (resource URI) and the Windows Azure AD tenant I want to work with.
Given that I want the authentication flow to take place in its own page, I need a way to get those coordinates in that page; furthermore, I need to extract the access token (and whatever else I might want, like the refresh token) from the sign in page and make it available to the calling page.
I could pass those parameters in the querystring of the page on navigation events, but I’ll go with the brute force approach instead: I’ll shove all important settings in global properties. Below you can see the main ones, in App.xaml.cs.
public partial class App : Application { /// <summary> /// Provides easy access to the root frame of the Phone Application. /// </summary> /// <returns>The root frame of the Phone Application.</returns> public static PhoneApplicationFrame RootFrame { get; private set; } public string RedirectUri {get; set;} public string DomainName {get; set;} public string ClientID {get; set;} public string Resource {get; set;} public string Code {get; set;} public string AccessToken {get; set;}
Given that I just want to see the flow work, I won’t add any fancy configuration and initialize the scenario coordinates as hardcoded values at construction time:
/// <summary> /// Constructor for the Application object. /// </summary> public App() { // Global handler for uncaught exceptions. UnhandledException += Application_UnhandledException; // ...default stuff omitted for brevity, keep everything as you find it RedirectUri = "http://whatevah"; DomainName = "treyresearch1.onmicrosoft.com"; ClientID = "4a491cff-73a9-4a45-b274-b5836a723b14"; Resource = "http://localhost:8643/"; Code = AccessToken = string.Empty; }
The Main Page
With the coordinates being taken care of, let’s focus to the main page from the template. Let’s add the couple of buttons I mentioned earlier and hook them up to click event handlers stubs.
Then, let’s move to the C# code for the page.
public partial class MainPage : PhoneApplicationPage { App app; // Constructor public MainPage() { InitializeComponent(); app = App.Current as App; }
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); if (app.AccessToken != string.Empty) { signInbtn.Visibility = System.Windows.Visibility.Collapsed; callSvcbtn.IsEnabled = true; } else { signInbtn.Visibility = System.Windows.Visibility.Visible; callSvcbtn.IsEnabled = false; } }
Before everything, I added a shorthand for the application object to make access to coordinates and tokens handy.
Then, I added some UI management logic in the OnNavigatedTo event. The idea is simple: when there is no token in the global property, I need to display the sign in button (and I cannot call the service just yet, hence the corresponding button must be disabled).
Conversely, if I do have a token I should no longer display the sign in button and I should ensure that the service calling button is enabled.
The above is really really coarse: the flow does provide me with expiration data and a refresh token, which I could use for a more sophisticated token lifecycle management, but I wanted to do the bare minimum.
Done the above, let’s move to the service calling logic. Below you can find the familiar OAuth2 bearer-protected REST call calling logic.
private void callSvcbtn_Click(object sender, RoutedEventArgs e) { HttpWebRequest hwr = WebRequest.Create(new Uri("http://192.168.1.42:8643/"+"api/todolist")) as HttpWebRequest; hwr.Headers[System.Net.HttpRequestHeader.Authorization] = "Bearer " + app.AccessToken; hwr.BeginGetResponse(GetResponseCallback,hwr); } private void GetResponseCallback(IAsyncResult rez) { HttpWebRequest hwr = rez.AsyncState as HttpWebRequest; HttpWebResponse response = hwr.EndGetResponse(rez) as HttpWebResponse; string a = (new StreamReader(response.GetResponseStream(), Encoding.UTF8)).ReadToEnd(); Dispatcher.BeginInvoke(() => { MessageBox.Show(a); }); }
The click handler initializes a new request for the intended API and add to it the access token as the authorization header. You might have noticed that I am using an IP address in the resource URL: the reason is that in order to make an app running in the WP8 emulator see the IIS Express on the local machine you have to go through some extra config, which ends up entailing using the IPv4 address of your dev box. Instructions here!
The GetResponseCallback method retrieves the response, and (courtesy of the dispatcher, super-handy when you want stuff to happen in the UI thread) displays it in a dialog.
Like the rest of the post, everything naively without any error management.
The last thing to fill in is the handler for the sign in button:
private void signInbtn_Click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/SignIn.xaml",UriKind.Relative)); }
Yep, it looks like I need to add a page called SignIn.
The Sign In Page
Below you can see the sign in page, which I added to the main project.
Apart from the omnipresent titles, the page contains… a WebBrowser object. As you can imagine, the job of that browser will be to render the UI required by the Authorization endpoint. Let’s take a look at the code, piece by piece.
public partial class SignIn : PhoneApplicationPage { App app = (App.Current as App); public SignIn() { InitializeComponent(); myBrowser.IsScriptEnabled = true; } protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); string authURL = string.Format( "https://login.windows.net/{0}/oauth2/authorize?response_type=code&resource={1}&client_id={2}&redirect_uri={3}", app.DomainName,
app.Resource,
app.ClientID,
app.RedirectUri); //navigate to it myBrowser.Navigate(new Uri(authURL)); }
The page creates the usual shorthand for the app object.
The constructor ensures that the WebBrowser will support JavaScript (off by default).
The page event OnNavigatedTo kicks in the sign in flow as soon as the page is reached.
First, it creates the Windows Azure AD preview Authorization endpoint string, by instantiating in the current URL template the necessary coordinates; then it passes it to the browser control to navigate to it.
Form this moment on, all the action will be triggered by the events raised by the browser control. Namely: the user will interact with whatever credential gathering will be presented by Windows Azure AD, and at the end of the Authorize portion of the flow the browser will be redirected to the return URI. Given that such URI might not correspond to an actual endpoint (as it is the case here) we need to detect that the flow is heading there before the browser attempts to render a non-existent page: we do that by checking for the return URI in the Navigating event.
private void Navigating(object sender, NavigatingEventArgs e) { string returnURL = e.Uri.ToString(); if (returnURL.StartsWith(app.RedirectUri)) { app.Code = e.Uri.Query.Remove(0,6); e.Cancel = true; myBrowser.Visibility = System.Windows.Visibility.Collapsed; GetToken(); } }
The code is really easy: check if the requested URL begins with the return URI, and if it does extract the code (by clipping ‘?code=’ from the query), stop the browser navigation, hide the browser control and trigger the next stage of the token acquisition logic (in the custom method GetToken).
private void GetToken() { HttpWebRequest hwr = WebRequest.Create( string.Format("https://login.windows.net/{0}/oauth2/token", app.DomainName)) as HttpWebRequest; hwr.Method = "POST"; hwr.ContentType = "application/x-www-form-urlencoded"; hwr.BeginGetRequestStream(new AsyncCallback(SendTokenEndpointRequest), hwr); }
GetToken prepares a new POST request for the Windows Azure AD token endpoint, then leaves to SendTokenEndpointRequest the asynchronous task of pumping up the request bits.
private void SendTokenEndpointRequest(IAsyncResult rez) { HttpWebRequest hwr = rez.AsyncState as HttpWebRequest; byte[] bodyBits = Encoding.UTF8.GetBytes( string.Format( "grant_type=authorization_code&code={0}&client_id={1}&redirect_uri={2}", app.Code, app.ClientID, HttpUtility.UrlEncode(app.RedirectUri))); Stream st = hwr.EndGetRequestStream(rez); st.Write(bodyBits, 0, bodyBits.Length); st.Close(); hwr.BeginGetResponse(new AsyncCallback(RetrieveTokenEndpointResponse), hwr); }
SendTokenEndpointRequest prepares the body of the request by crafting the message that the Token endpoint expects, including the client coordinates (minus the secret, given that this is not a confidential client just like an Windows Store app) and the code received form the authorization endpoint.
That done, it pumps it to the Token endpoint and leaves to RetrieveTokenEndpointResponse the asynchronous task of retrieving the bits of the response.
private void RetrieveTokenEndpointResponse(IAsyncResult rez) { HttpWebRequest hwr = rez.AsyncState as HttpWebRequest; HttpWebResponse resp = hwr.EndGetResponse(rez) as HttpWebResponse; StreamReader sr = new StreamReader(resp.GetResponseStream()); string responseString = sr.ReadToEnd(); JObject jo = JsonConvert.DeserializeObject(responseString) as JObject; app.AccessToken = (string)jo["access_token"]; Dispatcher.BeginInvoke(() => { NavigationService.GoBack(); }); }
RetrieveTokenEndpointResponse deserializes the response in a string, then (thanks to the magical Json.NET: I love that library!) parses it in a JSON object and extracts from it the access token. The access token is then saved in the global property, ready to be used by the service calling method in the main page.
The method closes with another UI thread operation: now that the token has been obtained, the app can go back to the main page where it will be put to use, concluding the logic we need to complete the application flow.
Note: I extract just the access token because I am lazy, but the Token endpoint response contains everything you need:
{ "access_token": "eyJ0eXAiOiJKV1QiLBJhbGciOiJSUzI1NiI[…]jvsuXYGT1AvwN3BLdwvEG-vxsnSFIgRc_T6gYGtQgYBQezhLkvRA", "token_type": "Bearer", "expires_in": "28799", "expires_on": "1367336769", "refresh_token": "AAAAAAAAB0muTENxe1qIJDFMMyUMiFt[…]Q-Hq3WRqO3XUgAA", "scope": "user_impersonation" }
Just wanted to make sure you don’t forget what’s in there. That said, let’s see this baby in action!
Running the Project
As mentioned above, running this solution in the emulator requires doing some extra work to make IIS Express visible: all the instructions are here.
That said, let’s have some fun!
The idea is that on F5 the solution will start all projects: the Windows Store client, the Web API and the WP8 client. We’ll use the Windows Store client for creating some todo items, then we’ll use the WP8 client to read them.
Here there’s the test page of the Web API…
…and here there’s the Windows Store client from the original sample. Let’s use it to add some entries (not exactly todo’s, but yesterday I had an earworm… do you have it too now? ).
Great! Let’s do few ALT+TAB to get to the emulator, already loaded with our app (titled as the Spanish conversations between Caleb and Sean ).
Hitting sign in lands us to the Windows Azure authorization endpoint page, which in turn bounces us around to gather user credentials. Note: the browser is purposefully smaller than the entire page for showing you what’s going on here, otherwise you might confuse it with a full browser handover.
Entering the right credential makes the magic described above happen, then bounces us back to the main page. From the fact that the sign in button is gone and the call service button is active we can tell that the access token property is non-empty.
If we click the call service button, ta-dah! We get back the todos uploaded from the other client.
Q.E.D.
Summary
Yes, I agree: a library would be faster & easier to use, hopefully more robust and less code-invasive. However if you want to take advantage of Windows Azure AD from your Windows Phone 8 applications hitting the protocol endpoints is definitely an option!
This would be a good time to remind you that the code I write here is not official guidance and really does not want to be: it’s just stuff I like to do when the TV is not giving me all the ROI I’d expect from that fancy cable box… take inspiration if you want, but please make sure to do due diligence before doing anything more than experimentations with it.
That said, writing apps for devices is FUN! Have a good time and keep the feedback coming
Fun With Windows Azure AD: Calling REST Services from a Windows Phone 8 App | CloudIdentity