Итак, сегодня мы продолжаем разбираться с различными механизмами авторизации пользователей в приложениях на Xamarin. После знакомства с SDK от Facebook и ВКонтакте (здесь и здесь), можем перейти к одному из самых популярных (на текущий момент) механизмов внешней авторизации пользователей — OAuth. Большинство популярных сервисов вроде Twitter, Microsoft Live, Github и так далее, предоставляют своим пользователям возможность входа в сторонние приложения с помощью одного привычного аккаунта. Научившись работать с OAuth вы легко сможете подключать все эти сервисы и забирать из них информацию о пользователе.

Все статьи из колонки можно найти и прочитать по ссылке #xamarincolumn, или в конце материала под катом.




Предполагается, что вы уже знакомы с тем, как работает OAuth, а если нет — рекомендуем вот эту хорошую статью на Хабре. Если коротко, то при авторизации OAuth пользователь перенаправляется с одной веб-страницы на другую (обычно 2-3 шага) до тех пор, пока не перейдет на конечный URL. Этот финальный переход и будет отловлен в приложении (если писать логику самому) на уровне WebView, а нужные данные (token и срок его валидности) будут указаны прямо в URL.


Небольшой список популярных сервисов, которые предоставляют возможность авторизации пользователей по OAuth: Одноклассники, Mail.ru, Dropbox, Foursquare, GitHub, Instagram, LinkedIn, Microsoft, Slack, SoundCloud, Visual Studio Online, Trello.


Xamarin.Auth


Для того, чтобы работать с OAuth в Xamarin мы остановимся на простой и удобной библиотеке Xamarin.Auth, которая развивается уже не первый год и имеет все необходимые для нас механизмы:


  1. Отображение браузера со страницами авторизации
  2. Управление потоком редиректов и процессом авторизации
  3. Получение нужных данных
  4. Предоставление механизмов для дополнительных запросов к сервису, например для получения информации о пользователе

Также Xamarin.Auth поддерживает возможность хранения учетных данных пользователя в защищенном хранилище. В общем, зрелый и качественный компонент с необходимой функциональностью.


Рекомендуем устанавливать Xamarin.Auth из Nuget, так как версия в Xamarin Components уже давно устарела и не обновляется.




Напомню, что мы уже ранее рассказывали про авторизацию с помощью SDK от Facebook и ВКонтакте. В нашем примере мы вынесли всю логику авторизации в платформенные проекты, оставив в PCL только интерфейсы. Для OAuth мы пойдем тем же путем, несмотря на поддержку PCL в самом Xamarin.Auth.


Помимо Xamarin.Auth можем также порекомендовать библиотеку Xamarin.Forms.OAuth от Bruno Bernardo. Даже если вы используете классический Xamarin, в исходных кодах этого проекта можно найти множество готовых конфигураций для различных сервисов.


Мы же в качестве примера работы OAuth подключим авторизацию с помощью Microsoft. Первым делом создадим приложение на сайте https://apps.dev.microsoft.com и получим там Client ID (ИД клиента или приложения).



Подключаем авторизацию в PCL


На уровне PCL все как обычно — делаем простой интерфейс IOAuthService для платформенного сервиса, никаких новых зависимостей в проект не добавляем.


public interface IOAuthService
{
   Task<LoginResult> Login();
   void Logout();
}

Ну и, конечно же, будет необходимо добавить обращение к методам DependencyService.Get<IOAuthService>().Login() и DependencyService.Get<IOAuthService>().Logout() внутри нашей страницы авторизации.


Также нет проблем добавить поддержку нескольких OAuth-сервисов. Для этого можно добавить в методы Login() и Logout() аргумент providerName (тип string, int или enum) и в зависимости от его значения выбирать поставщика услуг.


Реализация платформенной части


Как уже отмечалось ранее, необходимо добавить библиотеки Xamarin.Auth из Nuget в каждый платформенный проект, в нашем случае — iOS и Android. Дальше пишем нашу реализацию IOAuthService для каждой платформы и регистрируем ее в качестве Dependency.


Теперь нам достаточно создать экземпляр класса OAuth2Authenticator с нужными параметрами:


         var auth = new OAuth2Authenticator
           (
               clientId: "ВАШ_CLIENT_ID",
               scope: "wl.basic, wl.emails, wl.photos",
               authorizeUrl: new Uri("https://login.live.com/oauth20_authorize.srf"),
               redirectUrl: new Uri("https://login.live.com/oauth20_desktop.srf"),
               clientSecret: null,
               accessTokenUrl: new Uri("https://login.live.com/oauth20_token.srf")
           )
           {
               AllowCancel = true
           };

Теперь повесим обработчик завершения авторизации:


auth.Completed += AuthOnCompleted;

Всё, можно показать модальное окно со встроенным веб-браузером для авторизации, получаемое через метод auth.GetUI(). На iOS это можно сделать примерно так:


UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(auth.GetUI(), true, null);

На Android при использовании Xamarin.Forms код может получится следующим:


Forms.Context.StartActivity(auth.GetUI(Forms.Context));

После успешной авторизации вызовется наш метод AuthOnCompleted(), и для iOS будет необходимо скрыть модальное окно с браузером (на Android само скроется):


UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);

Теперь можно получать нужные данные (access_token и время его жизни в секундах — expires_in)


var token = authCompletedArgs.Account.Properties["access_token"];                
var expireIn = Convert.ToInt32(authCompletedArgs.Account.Properties["expires_in"]);
var expireAt = DateTimeOffset.Now.AddSeconds(expireIn);

И нам остался последний шаг — получить расширенную информацию из профиля пользователя, включая email и ссылку на аватарку. Для этого в Xamarin.Auth есть специальный класс OAuth2Request с помощью которого удобно делать подобные запросы.


             var request = new OAuth2Request("GET", new Uri("https://apis.live.net/v5.0/me"), null, account);
var response = await request.GetResponseAsync();

Теперь нам приходит JSON с данными пользователя, и мы можем их сохранить и отобразить в приложении.


             if (response.StatusCode == HttpStatusCode.OK)
{
    var userJson = response.GetResponseText();
    var jobject = JObject.Parse(userJson);
    result.LoginState = LoginState.Success;
    result.Email = jobject["emails"]?["preferred"].ToString();
    result.FirstName = jobject["first_name"]?.ToString();
    result.LastName = jobject["last_name"]?.ToString();
    result.ImageUrl = jobject["picture"]?["data"]?["url"]?.ToString();
    var userId = jobject["id"]?.ToString();
    result.UserId = userId;
    result.ImageUrl = $"https://apis.live.net/v5.0/{userId}/picture";
}

Как видим, ничего сложного нет. Вопрос в том, чтобы правильно прописать URL для процесса авторизации. Ну и помнить, что поле expires_in содержит время в секундах (это вызывает частые вопросы).




В реальных проектах также рекомендуем назначить обработчик ошибок на событие auth.Error, чтобы ни одна проблема не осталась без решения.


Заключение


Сегодня мы завершили рассмотрение всех популярных способов авторизации пользователей и получения базовой информации о них через внешние сервисы. Описанные механизмы подходят как для Xamarin.Forms, так и для классического Xamarin iOS/Android. Полные исходные коды проекта со всеми примерами можно найти в нашем репозитории:


https://bitbucket.org/binwell/login


Задавайте ваши вопросы в комментариях к статье и оставайтесь на связи!



Об авторе


Вячеслав Черников — руководитель отдела разработки компании Binwell, Microsoft MVP и Xamarin Certified Developer. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone. Статьи Вячеслава вы также можете прочитать в блоге на Medium.

Другие статьи автора:

Поделиться с друзьями
-->

Комментарии (4)


  1. snovikov
    14.07.2017 09:07

    Мне кажется, что в статье всё-таки шла речь об аутентификации, а не об авторизации. Или я что-то упустил?


    1. devious
      14.07.2017 09:28

      Да, вы правы, корректнее было бы во многих местах использовать термин «аутентификация», хотя и конечная цель — все таки «авторизация» запросов.


      1. snovikov
        15.07.2017 01:13
        +1

        А планируете продолжить тему и написать статью именно про авторизацию запросов?


        1. devious
          17.07.2017 11:28

          Про саму авторизацию — все будет зависеть от того, REST вы используете, SOAP или что-то еще. В любом случае там все решается достаточно просто на уровне HTTP-заголовков или параметров URL. Если вы используете REST, то мы уже описывали работу с ним на Xamarin.Forms с помощью Refit и там можно легко добавить нужные механизмы авторизации: https://habrahabr.ru/company/microsoft/blog/310704/