Xamarin: Авторизация OAuth для Xamarin-приложений

Восьмая статья в цикле “Рецепты для Xamarin-разработчиков”.

Оригинал: https://habrahabr.ru/company/microsoft/blog/332970/

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

Предполагается, что вы уже знакомы с тем, как работает 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

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

Like what you read? Give Slava Chernikoff a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.