Многофакторная аутентификация, ставшая абсолютно естественной мерой обеспечить безопасность популярных пользовательских сервисов, стремительно проникает и в корпоративный сегмент. В этой статье мы рассмотрим, как добавить в мобильное приложение возможность еще одного подтверждения аутентификации, в дополнение к имени пользователя и паролю, например звонок на зарегистрированный на пользователя телефон или ввод кода из СМС. Помимо этого, будет рассмотрена настройка backend-сервисов для реализации данной многофакторной аутентификации.
 

Профиты многофакторной аутентификации для корпоративных приложений

  1. Более надежная аутентификация, чем только имя пользователя и пароль. К «что-то, что я знаю» (логин/пароль) добавляется «что-то, чем обладаю» — физическое устройство, а проверить, владеет ли им данный пользователь, позволяет код в СМС, отправленной на это устройство, или звонок на него с запросом пин-кода.
  2. Удобное управление настройками подобной системы аутентификации — включение/выключение разной степени защиты для разных пользователей, настройка различных политик и способов работы дополнительных факторов аутентификации.
  3. Возможность использовать учетные данные корпоративной инфраструктуры для входа в приложения, а не создавать новые логины и пароли для приложения в дополнение к корпоративным.

Чем отличается аутентификация от идентификации?

Загрузка ... Загрузка ...

Мы рассмотрим реализацию этой задачи с помощью следующих продуктов и технологий:

  • Azure Active Directory — облачный сервис многофакторной аутентификации;
  • Azure Active Directory Authentication Libraries (ADAL) — библиотеки, обеспечивающие вызовы сервисов многофакторной аутентификации, включая проверки сертификатов, защищенное соединение с облачным сервисом аутентификации, отображение необходимых диалогов аутентификации. Поддерживает множество платформ и языков программирования;
  • Xamarin Forms — кросс-платформенное средство создания мобильных приложений, обеспечивающее компиляцию в нативный код и стопроцентный доступ к нативным API. Особенно удобно для быстрого создания приложений с переиспользованием 80–90% и более исходного кода между iOS, Android, Windows UWP — как раз наш случай для данного примера. Xamarin доступен как бесплатная часть Microsoft Visual Studio во всех ее версиях, включая бесплатную Visual Studio Community Edition.

Также с помощью Active Directory Federation Services (ADFS) и их интеграции с Azure Active Directory мы можем использовать для многофакторной аутентификации корпоративные учетные записи и пароли внутреннего домена Active Directory, но основной упор в этой статье сделаем на первые две задачи. ADFS будет опциональной задачей, так как, если нам не нужна интеграция с локальным доменом Active Directory, мы можем создать учетные записи вручную непосредственно в сервисе Azure Active Directory.

Начнем с настройки Azure Active Directory: если у нас нет учетной записи и доступа к Microsoft Azure, можем легко получить бесплатную пробную учетную запись здесь. В рамках пробной подписки большое число сервисов предоставляется бесплатно на год. В целом все, что описано в данной статье, ты можешь попробовать бесплатно.

 

Регистрация мобильного приложения для доступа к Azure Active Directory

Первым шагом мы регистрируем мобильное приложение в Azure Active Directory для того, чтобы получить доступ к сервису. Фактически мы создаем учетную запись приложения с определенными идентификаторами и настройками безопасности. Только мобильное приложение, которое через защищенное соединение сможет предъявить данные идентификаторы, получит доступ к службе аутентификации.

Зайдя на портал Azure, в самой левой черной панели служб выбираем службу Azure Active Directory (или нажимаем All Resources и пользуемся поиском служб), переходим в подраздел App registrations и регистрируем новую учетную запись приложения, нажимая New application registration.

New App Registration
New App Registration

Заполняем поля новой учетной записи приложения:

  • Name — любое имя, по которому потом нам самим будет понятно, о каком приложении речь;
  • Application Type — выбираем Native, так как у нас будет мобильное приложение, а не веб-сервис;
  • Redirect URI — это фактически идентификатор, который должен обязательно совпадать в настройке учетной записи приложения на стороне сервиса с настройкой на стороне клиента. Это может быть любой произвольный URI (произвольная строка в формате URI) — он не должен быть обязательно реальным хостом и не должен быть зарегистрирован в DNS.
New App Registration Form
New App Registration Form

Нажимаем Create и получаем новую учетную запись приложения с автоматически назначенным системой идентификатором, который называется APPLICATION ID.

New App Registration Completed
New App Registration Completed

Это очень важный идентификатор — его обязательно должно передать мобильное приложение вместе с совпадающим с настройкой сервиса Redirect URI, чтобы получить доступ к службе аутентификации.

 

Создание пользователя и включение многофакторной аутентификации

Теперь мы создадим пользователя, пароль и включим для него многофакторную аутентификацию.

Напомню, что, если есть задача использовать учетные записи, уже существующие в организации в локальном домене Active Directory, это можно сделать с помощью службы Active Directory Federation Services. Тогда нам не нужно будет заводить пользователей, можно будет настраивать многофакторную аутентификацию для уже существующих в домене пользователей, и при входе в мобильное приложение они будут использовать свои доменные логины и пароли.

То есть при использовании AFDS получится такая топология:

ADFS Architecture
ADFS Architecture

Мы же продолжим базовый сценарий, который не требует наличия домена Active Directory и настройки службы ADFS, и заведем пользователя самостоятельно в Azure Active Directory.

Заходим в знакомый раздел Azure Active Directory → Users and groups → All users и нажимаем New user.

New User
New User

Далее заполняем информацию о пользователе. Обрати внимание на User name: если у тебя в подписке Azure сконфигурировано свое доменное имя — используй его, если нет — система автоматически сгенерирует доменное имя в алиасе пользователя по принципу [логин нашей учетной записи Azure].onmicrosoft.com. Для целей тестирования этого достаточно, для производственных систем, очевидно, мы пропишем в подписке Azure свой более симпатичный домен. Также придумываем и прописываем пароль. На скриншоте подчеркнут автоматически сгенерированный домен для моей пробной подписки.

New User Form
New User Form

Нажимаем Create и получаем новую учетную запись пользователя.

Теперь включим для нее многофакторную аутентификацию. Заходим в раздел All users и нажимаем кнопку Multi-Factor Authentication.

Multi-Factor Authentication Button
Multi-Factor Authentication Button

Дальше мы попадаем в настройки многофакторной аутентификации, выбираем нужного пользователя и нажимаем Enable.

Enabling MFA
Enabling MFA

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

MFA Success
MFA Success

На стороне сервиса все готово — данный пользователь теперь будет аутентифицироваться многофакторно. Если хочется изменить настройки сервиса, можно зайти в раздел Service settings — сразу под надписью Multi-factor authentication на предпоследнем экране выше.

Здесь есть, например, интересный раздел, который задает, какие методы дополнительных «факторов» доступны пользователю.

MFA Methods
MFA Methods

Здесь можно настроить звонок либо СМС на телефон или подтверждение через мобильное приложение Microsoft Authenticator.

Конфигурирование сервиса Azure Active Directory завершено, теперь создадим мобильное приложение и интегрируем в него многофакторную аутентификацию.

 

Интеграция многофакторной аутентификации в мобильное приложение

Как я сказал выше, мы для примера возьмем Xamarin Forms (бесплатная часть Visual Studio), который позволит нам, переиспользуя большую часть кода, получить сразу нативные мобильные приложения с поддержкой многофакторной аутентификации для iOS, Android и Windows UWP.

В чем слабость двухфакторной аутентификации с подтверждением по SMS?

Загрузка ... Загрузка ...

Быстрый способ — используем готовый исходный код

  1. Забираем код из моего репозитория на GitHub.
  2. Прописываем в код приложения в файле MainPage.xaml.cs идентификаторы, которые мы сконфигурировали при регистрации приложения в облачном сервисе Azure Active Directory. Их полное соответствие в приложении и сервисе необходимо, чтобы сервис не отклонял обращения приложения.

clientID — это Application ID из наших настроек облачного сервиса выше.

public static string clientId = "<<INSERT YOUR CLIENT ID HERE>>";

returnURI — это Redirect URI из наших настроек облачного сервиса выше.

public static string returnUri = "http://MFATestPCL-redirect";

Все готово. Теперь мы рассмотрим также создание приложения с нуля.

Детальный способ — создаем приложение с нуля

Запускаем Visual Studio (я сейчас использую VS 2017, версия 15.6.7) с установленным Xamarin. Это можно проверить, запустив Visual Studio Installer и нажав Modify текущей инсталляции Visual Studio.

Xamarin Module
Xamarin Module

Xamarin входит во все версии Visual Studio — даже в бесплатную Community Edition, но нужно убедиться, что он включен и установлен, как показано выше.

Запускаем Visual Studio и создаем новый проект: меню File → New → Project. Выбираем Cross-Platform → Mobile App (Xamarin.Forms).

New Project
New Project

Далее выбираем Blank App и .NET Standard как Code Sharing Strategy.

Code Sharing
Code Sharing

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

Solution
Solution

Если это не так, сделаем его стартовым: правый клик на проекте App.Android и меню Set as a StartUp Project.

Нажав F5, запустим приложение на эмуляторе Android и увидим:

New App
New App

Убедившись, что все работает, останавливаем отладку (Shift + F5).

Чтобы интегрировать многофакторную аутентификацию, подключим библиотеку Azure Active Directory Authentication Libraries (ADAL). Подключить ее надо будет к каждому проекту: правый клик на каждом проекте — всего четыре проекта (общий + специфичный под каждую из трех платформ) = 4 раза → Manage NuGet Packages.

Manage NuGet
Manage NuGet

Далее закладка Browse — вводим в поиск adal и подключаем, нажимая на кнопку со стрелкой вниз.

Adal Add
Adal Add

Для реализации логики работы с библиотекой будем использовать следующий подход:

  • в общем проекте определим интерфейс IAuthenticator, через который будем вызывать аутентификацию из общего кода;
  • в каждом из платформенных проектов определим специфичную для конкретной платформы реализацию взаимодействия аутентификации с пользовательским интерфейсом этой платформы;
  • используя DependencyService, доступный в Xamarin Forms, мы сможем при вызове единого метода Authenticate интерфейса IAuthenticator из общего кода автоматически использовать именно ту реализацию под конкретную платформу, на которой в данный момент запущено приложение.

Начинаем с определения интерфейса IAuthenticator в общем для всех платформ проекте, создаем в нем новый файл IAuthenticator.cs с определением интерфейса (правый клик на проекте → Add → New Item → Class).

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;

namespace App17
{
    public interface IAuthenticator
    {
        Task<AuthenticationResult> Authenticate(string authority, string resource, string clientId, string returnUri);
    }
}

Как видишь, мы определяем сигнатуру асинхронного метода аутентификации. AuthenticationResult при успешной аутентификации будет содержать полученный от Azure Active Directory токен. Соберем проекты: правый клик на Solution → Build Solution.

Теперь создаем реализацию этого интерфейса для каждой из платформ. Он будет очень похож — основываться на вызове метода AcquireTokenAsync; единственное различие, почему его, собственно, и приходится создавать платформенно-зависимым, в специфике интеграции с платформенным UI через PlatformParameters для отображения всплывающих веб-диалогов аутентификации.

Насколько безопасно использовать в своем проекте готовые фрагменты чужого кода?

Загрузка ... Загрузка ...
Android

Для Android нам нужно инициализировать PlatformParameters текущим окном/диалогом, то есть в терминах Android — Activity. Для этого мы можем использовать Forms.Context. Добавляем в Android-проект файл Helper.cs со следующим кодом.

using System;
using System.Linq;
using System.Threading.Tasks;
using Android.App;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Xamarin.Forms;

[assembly: Dependency(typeof(App17.Droid.Helper.Authenticator))]
namespace App17.Droid.Helper
{
    class Authenticator : IAuthenticator
    {
        public async Task<AuthenticationResult> Authenticate(string authority, string resource, string clientId, string returnUri)
        {
            var authContext = new AuthenticationContext(authority);
            if (authContext.TokenCache.ReadItems().Any())
                authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);

            var uri = new Uri(returnUri);
            var platformParams = new PlatformParameters((Activity)Forms.Context);
            var authResult = await authContext.AcquireTokenAsync(resource, clientId, uri, platformParams);
            return authResult;
        }
    }
}

Если VS будет подчеркивать красным IAuthenticator — запусти сборку, чтобы проверить, что собирается успешно.

Также обрати особое внимание на метаатрибут Dependency для данного Helper namespace — именно он позволяет DependencyService сопоставить вызов из общего кода и реализацию для конкретной платформы.

[assembly: Dependency(typeof(App17.Droid.Helper.Authenticator))]

Также для Android нам нужно переопределить OnActivityResult метод в MainActivity.cs файле (внутри класса MainActivity) для правильной обработки и продолжения последовательности диалогов аутентификации (также добавляем два namespace через директивы using).

using Android.Content;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);
            AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
        }

Теперь переходим к iOS.

iOS

В iOS для PlatformParameters передаем UIViewController в качестве контекста, а именно RootViewController — то есть текущее окно.

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using UIKit;
using Xamarin.Forms;

[assembly: Dependency(typeof(App17.iOS.Helper.Authenticator))]
namespace App17.iOS.Helper
{

    class Authenticator : IAuthenticator
    {
        public async Task<AuthenticationResult> Authenticate(string authority, string resource, string clientId, string returnUri)
        {
            var authContext = new AuthenticationContext(authority);
            if (authContext.TokenCache.ReadItems().Any())
                authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);

            var controller = UIApplication.SharedApplication.KeyWindow.RootViewController;
            var uri = new Uri(returnUri);
            var platformParams = new PlatformParameters(controller);
            var authResult = await authContext.AcquireTokenAsync(resource, clientId, uri, platformParams);
            return authResult;
        }
    }

}

Не забываем про атрибут Dependency перед namespace. Для iOS не нужно переопределять метод возврата результатов окна/диалога.

Для того чтобы собрать iOS-проект, нам понадобится Mac-хост с агентом сборки или, например Mac-машина в облаке для сборки, которую предоставляет Microsoft App Center (опять-таки можно попробовать бесплатно — до 240 минут сборки в месяц плата не взимается).

Теперь реализация для Windows UWP (Universal Windows Platform).

Universal Windows Platform

Здесь тоже все очень просто — добавляем знакомый файл Helper.cs с реализацией интерфейса IAuthenticator, не забывая про атрибут Dependency.

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;

[assembly: Dependency(typeof(App17.UWP.Helper.Authenticator))]
namespace App17.UWP.Helper
{
    class Authenticator : IAuthenticator
    {
        public async Task<AuthenticationResult> Authenticate(string authority, string resource, string clientId, string returnUri)
        {
            var authContext = new AuthenticationContext(authority);
            if (authContext.TokenCache.ReadItems().Any())
                authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);

            var uri = new Uri(returnUri);
            var platformParams = new PlatformParameters(PromptBehavior.Auto, false);
            var authResult = await authContext.AcquireTokenAsync(resource, clientId, uri, platformParams);
            return authResult;
        }
    }
}

Теперь вернемся в общий проект для добавления вызова аутентификации.

Общий проект

Добавим кнопку для входа с использованием многофакторной аутентификации в общий проект. Заходим в общий проект Xamarin Forms, в котором содержится общий код и общий пользовательский интерфейс для всех платформ, и открываем через Solution Explorer главную страницу приложения — MainPage.xaml.

Solution Main Page
Solution Main Page

Если Solution Explorer не видно, его можно включить через меню View → Solution Explorer.

Live Run
Live Run

Обрати внимание, что VS предложит запустить визуализацию интерфейса через Live Player, — нажми Live Run, и ты сможешь вживую видеть на эмуляторе прямо во время редактирования, как изменяется пользовательский интерфейс приложения. Очень удобно для разработки. Визуализация будет идти с некоторой задержкой, поэтому просто продолжай редактирование.

В Xamarin Forms вся логика пишется на языке C#, а визуализация на языке разметки XAML.

INFO

Познакомиться подробно с разработкой на Xamarin Forms можно, скачав бесплатную и очень подробную книгу знаменитого Чарльза Петцольда (думаю, многие помнят этого автора) «Creating Mobile Apps with Xamarin.Forms».

В основном Page (окне) приложения MainPage.xaml добавим кнопку с обработчиком, для этого XAML-код для кнопки (Button) поместим вместе с существующим Label внутрь контейнера StackPanel. Этот простейший контейнер выстраивает внутри себя контролы в ряд по вертикали или горизонтали.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App17"
             x:Class="App17.MainPage">
    <StackLayout Orientation="Vertical">
  <Label Text="Welcome to Xamarin.Forms!" 
           VerticalOptions="CenterAndExpand" 
           HorizontalOptions="Center"/>
        <Button x:Name="btnLogin" Text="Login to Azure Active Directory" Clicked="btnLogin_Clicked" VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

В Xamarin Forms язык XAML поддерживает множество различных контейнеров, причем их можно вкладывать друг в друга, создавая гибко масштабируемые интерфейсы под любые размеры экранов.

После этого сразу перейдем в файл MainPage.xaml.cs (для этого нужно развернуть секцию с MainPage.xaml, чтобы увидеть код/code-behind) и добавим обработчик нажатия кнопки, а также нужные нам идентификаторы и namespaces.

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using Xamarin.Forms;

namespace App17
{
    public partial class MainPage : ContentPage
    {
        public static string clientId = "<<INSERT YOUR CLIENT ID HERE>>";
        public static string authority = "https://login.windows.net/common";
        public static string returnUri = "http://MFATestPCL-redirect";
        private const string graphResourceUri = "https://graph.windows.net";
        private AuthenticationResult authResult = null;

        public MainPage()
        {
            InitializeComponent();
        }
        private async void btnLogin_Clicked(object sender, EventArgs e)
        {
            try
            {
                var auth = DependencyService.Get<IAuthenticator>();
                authResult = await auth.Authenticate(authority, graphResourceUri, clientId, returnUri);
                var userName = authResult.UserInfo.GivenName + " " + authResult.UserInfo.FamilyName;
                await DisplayAlert("Token", userName, "Ok", "Cancel");
            }
            catch (Exception ex)
            {
                await DisplayAlert("Error", ex.Message, "Ok");
            }
        }
    }
}

После этого прописываем в приложение в код идентификаторы, которые мы сконфигурировали при регистрации приложения в облачном сервисе Azure Active Directory. Их полное соответствие в приложении и сервисе необходимо, чтобы сервис не отклонял обращения приложения:

clientID — это Application ID из наших настроек облачного сервиса выше.

public static string clientId = "<<INSERT YOUR CLIENT ID HERE>>";

returnURI — это Redirect URI из наших настроек облачного сервиса выше.

public static string returnUri = "http://MFATestPCL-redirect";

Все готово.

Насколько надежно использование JSON Web Token?

Загрузка ... Загрузка ...

Запуск приложения

Теперь можно запускать приложение (например, на эмуляторе по F5) и аутентифицироваться учетными данными пользователя, которого мы завели выше. При первом входе после корректного ввода имени пользователя и пароля система предложит пользователю зарегистрировать свой мобильный номер для получения подтверждающих звонков или СМС — выбор будет зависеть от того, какие возможности многофакторной аутентификации мы разрешили в параметрах Service Settings настроек Multi-factor Authentication раздела облачного сервиса.

После регистрации мы получим звонок или СМС на мобильный телефон. Только после ввода с цифровой клавиатуры запрошенного подтверждения или СМС-кода мы успешно аутентифицируемся в системе.

При успешной аутентификации Azure Active Directory выдаст JWT-токен (JSON Web Token), который можно использовать для авторизации (разрешения) действий пользователя в мобильном приложении. Токен подписан цифровой подписью, подтверждающей его целостность, и содержит ряд полей, по которым мы можем определять не только то, что пользователь успешно прошел аутентификацию, но и к каким ролям он принадлежит. Для реализации высокой степени защиты и безопасности нужно дополнительно проверять сертификат, которым подписан токен, на то, что он действительный. Также токен нужно передавать только через защищенные соединения и хранить в защищенных хранилищах.

Если токен не получен — значит, пользователь аутентификацию не прошел и доступ к функциональности мобильного приложения разрешен быть не должен.

Полный пример приложения на GitHub содержит операцию выхода/logout, которая очищает кеш токенов в приложении, а также cookie, которые могут в соответствии с настройками сервиса Azure Active Directory позволять пользователю не вводить повторно пароль в течение настроенного времени.

Итак, если учетные данные верны и второй фактор подтвержден, мы успешно пройдем многофакторную аутентификацию и увидим данные из полученного токена подтвержденного пользователя.

iOS






Android






  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    4 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии