Hello!
One of the convenient features of Windows Azure Mobile Services is the use of external authentication services that are provided by Google, Facebook, Twitter and the Microsoft Account itself. This means that the developer, there is no need to implement work with each of them separately, for this there are unified methods.
')
Among the officially supported platforms are listed almost everything except ... the usual Windows Desktop.
What are we talking about
In the
official authentication documentation, the method of using this functionality is
described quite similarly; however, the example in its example is tied to platform-dependent Windows.UI.Popups. Does such a trifle really keep us from using this powerful tool in the design for a familiar environment that, moreover, still dominates? Let's figure it out.
Formulation of the problem
Displaying a modal window is necessary for:
- External provider web page display
- Getting user id from external provider to identify the user and authentication token from Azure - which is necessary for further requests to the backend.
- Returns the User ID to the MobileServiceUser instance, and authentication token to the MobileServiceClient instance.
Personally, I am against modal windows, so we’ll do everything in a single window using asynchronous methods.
Implementation of the idea
Before you begin, you must
register your application in your Microsoft Account to obtain a client code and a secret key, I will use the Microsoft provider as an example.
In the layout we need:
- WPF window with Frame, in which we will load the pages we need.
- A simple “LoginPage” page with a “Login” button.
- The second page is just a container for the WebBrowser component - “WebLoginPage”.
In the code, we implement an extension method for the MobileServiceClient class:
public static class DesktopWebLoginExtension { public async Task<MobileServiceUser> LoginAsync(this MobileServiceClient client, MobileServiceAuthenticationProvider provider, LoginPape loginPage) { Uri startUri = new Uri(client.ApplicationUri, "login/" + provider.ToString().ToLowerInvariant()); Uri endUri = new Uri(client.ApplicationUri, "login/done"); LoginResult externalAuthProviderLoginResult = await loginPage.GetLoginResultAsync(startUri, endUri); if (externalAuthProviderLoginResult == null) { return null; } var returnServiceUser = new MobileServiceUser(externalAuthProviderLoginResult.UserId); returnServiceUser.MobileServiceAuthenticationToken = externalAuthProviderLoginResult.AuthenticationToken; client.CurrentUser = returnServiceUser; return returnServiceUser; } }
We will analyze:
- As an additional parameter, our method accepts the loginPage - which of course is a gross violation of the software layer, but for example it will do.
- startUri and endUri are compiled from this documentation .
- loginPage.GetLoginResultAsync - an asynchronous method that will return an instance of the class LoginResult
- Next, we fill in the fields we need for further work.
And here is the container class itself, later it will be filled in from the JSON response:
public class LoginResult { public string UserId { get; set; } public string AuthenticationToken { get; set; } }
The code for the WebLoginPage page looks like this:
public class WebLoginPage : Page { private Uri startUri; private Uri endUri; public delegate void LoginCompleteEventHandler(LoginResult result); public event LoginCompleteEventHandler UserLogedIn; public LoginBrowserPage() { InitializeComponent(); webBrowser.Navigating += webBrowser_Navigating; } public Task<LoginResult> GetLoginResultAsync(Uri startUri, Uri endUri) { this.startUri = startUri; this.endUri = endUri; var returnTask = new TaskCompletionSource<LoginResult>(); webBrowser.Navigate(startUri); this.UserLogedIn += (r) => { returnTask.SetResult(r); }; return returnTask.Task; } internal void webBrowser_Navigating(object sender, NavigatingCancelEventArgs e) { if (e.Uri.Equals(this.endUri)) { string uri = e.Uri.ToString(); if (uri.LastIndexOf("#token=") != 0) { var startOfToken = uri.IndexOf("#token=") + "#token=".Length; uri = uri.Substring(startOfToken).Replace("%2C", ","); JObject jsonObj = JObject.Parse(uri); var userId = jsonObj["user"]["userId"].ToObject<string>(); var authToken = jsonObj["authenticationToken"].ToObject<string>(); UserLogedIn(new LoginResult() { UserId = userId, AuthenticationToken = authToken }); } else { UserLogedIn(null); } } } }
We will analyze:
- We announced an event that will occur upon completion of authentication in our mini browser.
- GetLoginResultAsync is a method that “waits” for the event to complete the authorization process using the excellent TaskCompletionSource class. So we have achieved a single “waiting line” from asynchronous methods. With a big stretch this can be called modal loading of the page into the frame with the return of the result to the calling function (asynchronously) - as Microsoft advises us to act with the advent of asynchronous methods.
- webBrowser_Navigating - in this event handler, we diligently deserialize the JSON response from the service and get the cherished UserId and AuthenticationToken.
- We call a cherished event - after which all asynchronous calls are completed and the caller receives the authentication result.
Now nothing prevents us from writing in the right place:
var user = await MobileServiceClient.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, webLoginPage);
PS At the end, our webBrowser will offer us to download the JSON response from the service. This IE engine problem is solved by just such a key in the registry:
Windows Registry Editor Version 5.00;
[HKEY_CLASSES_ROOT \ MIME \ Database \ Content Type \ application / json]
"CLSID" = "{25336920-03F9-11cf-8FD0-00AA00686F13}"
"Encoding" = hex: 08,00,00,00
Conclusion
Using the above translated code, we removed the unpleasant limitation associated with the inability to simply call MobileService.LoginAsync in the desktop application and get the authentication result asynchronously.
Thanks for attention.
References:
Windows Azure Mobile Services REST API Reference
Get started with authentication in Mobile Services
Register your application in Google