...

понедельник, 11 ноября 2013 г.

[Из песочницы] Используем внешнюю аутентификацию Windows Azure Mobile Services в десктопном WPF приложении


Здравствуйте!


Одной из удобных функций Windows Azure Mobile Services является использования внешних служб аутентификаций, которые предоставляются Google, Facebook, Twitter и сам Microsoft Account. Это означает, что разработчику, нет необходимости имплементировать работу с каждым из них отдельно, для этого есть унифицированные методы.


Среди официально поддерживаемых платформ числится почти все кроме… обычной Windows Desktop.



О чём речь




В официальной документации по аутентификации довольно подобно расписан метод использования этого функционала, однако, приведённый в нём примеру завязан на платформозависимый Windows.UI.Popups. Неужели такая мелочь может удержать нас от использования этого мощного инструмента в разработке для привычной среды, которая к тому же, все ещё доминирует? Давайте разбираться.

Постановка задачи




Отображение модального окна необходимо для:

  • Отображения Web-страницы внешнего провайдера

  • Получение user id от внешнего провайдера для идентификации пользователя и authentication token от Azure — который необходим для дальнейших запросов к бакенду.

  • Возврат User ID в экземпляр класса MobileServiceUser, а authentication token в экземпляр класса MobileServiceClient.




Лично я противник модальных окон, поэтому всё сделаем в едином окне, с применением асинхронных методов.

Реализация задумки




Перед тем как начать, вы должны зарегистрировать Ваше приложение у себя в Microsoft Account для получения кода клиента и секретного ключа, именно Microsoft-провайдер я буду использовать для примера.

В вёрстке нам понадобятся:



  1. WPF окошко с Frame, в который мы будем грузить нужные нам страницы.

  2. Простую страничку «LoginPage» c кнопкой «Login».

  3. Вторая страница — просто контейнер для WebBrowser компонента — «WebLoginPage».




В коде реализуем метод-расширения для класса MobileServiceClient:

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;
}
}


Разберём:



  1. Как дополнительный параметр наш метод принимает loginPage — что конечно является грубым нарушением software layer, но для примера сойдет.

  2. startUri и endUri собраны исходя из этой документации.

  3. loginPage.GetLoginResultAsync — асинхронный метод, который вернёт нам экземпляр класса LoginResult

  4. Далее происходит заполнение необходимых нам полей для дальнейшей работы.




А вот и сам класс-контейнер, в последствии он будет заполнен из JSON-ответа:

public class LoginResult
{
public string UserId { get; set; }
public string AuthenticationToken { get; set; }
}



Код страницы WebLoginPage выглядит вот так:



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);
}
}
}
}


Разберём:



  1. Мы объявили событие, которое будет возникать по завершении аутентификации в нашем мини браузере.

  2. GetLoginResultAsync — метод, который «ждёт» событие завершения процесса авторизации с помощью замечательного класса TaskCompletionSource. Так мы добились единой «линии ожидания» из асинхронных методов. С большой натяжкой это можно назвать модальной загрузкой страницы во фрейм с возвратом результата в вызывающую функцию (асинхронно) — как и советует нам действовать Microsoft с приходом асинхронных методов.

  3. webBrowser_Navigating — в этом обработчике события мы старательно десериализуем JSON-ответ от сервиса и получаем заветные UserId и AuthenticationToken.

  4. Вызываем заветное событие — после чего все асинхронные вызовы завершаются и вызывающий метод получает результат аутентификации.


Теперь ничего не мешает нам в нужном месте написать:



var user = await MobileServiceClient.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, webLoginPage);


P.S. При завершении наш webBrowser предложит нам скачать JSON ответ от сервиса. Это проблема IE движка решается вот таким ключиком в реестре:



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





Вывод




С помощью выше переведённого кода мы сняли неприятное ограничением связанное с невозможностью просто вызвать MobileService.LoginAsync в десктопном приложении и получить результат аутентификации асинхронно.

Спасибо за внимание.


Ссылки:



Windows Azure Mobile Services REST API Reference

Get started with authentication in Mobile Services

Register your application in Google

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий