If you’ve used the Web Authentication Broker (or WAB, as it is sometimes referred to as), you may know that it’s a little tricky to set up in a Windows Phone project. First, let me explain how the Web Authentication Broker (I’ll just call it the WAB from now on. It’s much easier to type) works.
The WAB is designed to allow you to integrate third-party sign in for your app. That could be to allow your app to access a user’s information on a service (i.e. Facebook or Twitter). On Windows Phone, you give the WAB a URI location to navigate to, and a callback URI to look for to know when the authentication process is complete. Then it will suspend your app and display the WAB, which is essentially a browser window that is sealed off from your app, so you can’t do any snooping and inject anything malicious into the website. The user can then go through the process of signing in to the service and granting your app access to their information. Once it is done, and the WAB detects the callback URI you provided, it will close the WAB and navigate back to your app, providing the access codes that the service provided.
The tricky part in all this is handling when your app is re-activated. In the samples Microsoft provides, they have a separate Continuation Manager class to handle the activation and will navigate your app to where it should go. This requires you to add code to the App.xaml.cs file to handle the activation. In my opinion, this whole process is unnecessarily convoluted and messy.
As a solution to this problem, and a way to make using the WAB across Windows and Windows Phone, I made a class I call WebAuthenticationBrokerHelper. (To be honest, my class mimics what Shawn Kendrot did to make picking files easier on Windows Phone. You can check his post out here.)
WebAuthenticationBrokerHelper consists of just two methods, only one of which you use, and a single property. First, there’s the TaskCompletionSource property:
1 |
private TaskCompletionSource<string> _authenticationCompletionSource; |
Next, there’s the AuthenticateAsync method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#if WINDOWS_PHONE_APP public Task<string> AuthenticateAsync(Uri authUrl, Uri callbackUrl) #else public async Task<string> AuthenticateAsync(Uri authUrl, Uri callbackUrl) #endif { #if WINDOWS_PHONE_APP _authenticationCompletionSource = new TaskCompletionSource<string>(); CoreApplication.GetCurrentView().Activated += WebAuthenticationBrokerHelper_Activated; WebAuthenticationBroker.AuthenticateAndContinue(authUrl, callbackUrl, null, WebAuthenticationOptions.None); return _authenticationCompletionSource.Task; #else WebAuthenticationResult result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, authUrl, callbackUrl); switch (result.ResponseStatus) { case WebAuthenticationStatus.Success: return result.ResponseData; case WebAuthenticationStatus.UserCancel: return "Cancelled"; case WebAuthenticationStatus.ErrorHttp: return result.ResponseErrorDetail.ToString(); default: return result.ResponseData; } #endif } |
The AuthenticateAsync method takes two URIs, the authentication URI and the callback URI. First it sets the TaskCompletionSource and connects to your app’s Activated event. (See below) Then it uses the proper WAB method based on the currently running platform to allow the user to authenticate.
Next up is the Activated event to handle when the app resumes after the WAB is done.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private void WebAuthenticationBrokerHelper_Activated(CoreApplicationView sender, IActivatedEventArgs args) { sender.Activated -= WebAuthenticationBrokerHelper_Activated; #if WINDOWS_PHONE_APP WebAuthenticationBrokerContinuationEventArgs wabArgs = args as WebAuthenticationBrokerContinuationEventArgs; WebAuthenticationResult result = wabArgs.WebAuthenticationResult; switch (result.ResponseStatus) { case WebAuthenticationStatus.Success: _authenticationCompletionSource.SetResult(result.ResponseData); break; case WebAuthenticationStatus.UserCancel: _authenticationCompletionSource.SetResult("Cancelled"); break; case WebAuthenticationStatus.ErrorHttp: _authenticationCompletionSource.SetResult(result.ResponseErrorDetail.ToString()); break; default: _authenticationCompletionSource.SetResult(result.ResponseData); break; } #endif } |
This is here so everything we need for the WAB is contained in one location. There’s no need to add code to your App.xaml.cs file or mess with a Continuation Manager.
Finally, here’s the complete class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public class WebAuthenticationBrokerHelper { private TaskCompletionSource<string> _authenticationCompletionSource; #if WINDOWS_PHONE_APP public Task<string> AuthenticateAsync(Uri authUrl, Uri callbackUrl) #else public async Task<string> AuthenticateAsync(Uri authUrl, Uri callbackUrl) #endif { #if WINDOWS_PHONE_APP _authenticationCompletionSource = new TaskCompletionSource<string>(); CoreApplication.GetCurrentView().Activated += WebAuthenticationBrokerHelper_Activated; WebAuthenticationBroker.AuthenticateAndContinue(authUrl, callbackUrl, null, WebAuthenticationOptions.None); return _authenticationCompletionSource.Task; #else WebAuthenticationResult result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, authUrl, callbackUrl); switch (result.ResponseStatus) { case WebAuthenticationStatus.Success: return result.ResponseData; case WebAuthenticationStatus.UserCancel: return "Cancelled"; case WebAuthenticationStatus.ErrorHttp: return result.ResponseErrorDetail.ToString(); default: return result.ResponseData; } #endif } private void WebAuthenticationBrokerHelper_Activated(CoreApplicationView sender, IActivatedEventArgs args) { sender.Activated -= WebAuthenticationBrokerHelper_Activated; #if WINDOWS_PHONE_APP WebAuthenticationBrokerContinuationEventArgs wabArgs = args as WebAuthenticationBrokerContinuationEventArgs; WebAuthenticationResult result = wabArgs.WebAuthenticationResult; switch (result.ResponseStatus) { case WebAuthenticationStatus.Success: _authenticationCompletionSource.SetResult(result.ResponseData); break; case WebAuthenticationStatus.UserCancel: _authenticationCompletionSource.SetResult("Cancelled"); break; case WebAuthenticationStatus.ErrorHttp: _authenticationCompletionSource.SetResult(result.ResponseErrorDetail.ToString()); break; default: _authenticationCompletionSource.SetResult(result.ResponseData); break; } #endif } } |
Now, wherever you want, you can just call:
1 |
string result = await wabHelper.AuthenticateAsync(apiAuthURI, apiRedirectURI)); |
And regardless of the platform you are on, the WAB will open and return the API’s response when it is done.
And there you have it. Thanks again to Shawn Kendrot for his original post. You can check out his website here.
Feel free to leave a comment with your questions, suggestions or feedback.
Thanks,
Joshua