Всем привет! Меня зовут Вадим, я старший разработчик по направлению Flutter в компании STM Labs. В этой статье я хочу поделиться нашим опытом внедрения единой биометрической системы в приложение, написанное на Flutter.

Для чего нужна единая биометрическая система?

Единая биометрическая система (ЕБС) — это государственная цифровая платформа, позволяющая идентифицировать человека по биометрическим характеристикам: голосу и лицу.

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

Не проще ли использовать стандартные методы для биометрической авторизации?

Конечно проще. Но биометрические данные, сохранённые на устройстве, не позволяют установить конкретное лицо для получения государственных или коммерческих услуг на территории Российской Федерации (local_auth для этой задачи не подойдёт).

Особенности интеграции ЕБС в мобильное приложение

Защита передачи данных

Для обеспечения безопасности работы с биометрическими образцами (БО), в соответствии с законодательством, на всех этапах требуется обеспечение шифрования данных с использованием алгоритма ГОСТ — включая создание защищённого посредством ГОСТ TLS-канала связи между МП и серверной частью. В нашем случае его обеспечивает специализированный дополнительный адаптер ЕСИА/ЕБС и КриптоПро NGate.

Отсутствие открытого API

ЕБС не раскрывает свой внутренний API для мобильных приложений, поэтому мы не можем оперировать всеми данными и методами. У нас есть только точка входа и точка выхода. На этом этапе возникает вопрос: что тогда интегрировать? Ответ прост — мы будем интегрировать адаптер для работы с клиентом системы.

Нужен свой испытательный/промышленный стенд

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

Часть 1. Постановка задачи

Для начала стоит понять, что из себя представляет ЕБС-адаптер для мобильного приложения? Это библиотека, которая обеспечивает:

  1. Проверку наличия установленного на устройстве пользователя приложения «Госуслуги Биометрия».;

  2. Взаимодействие клиентского приложения и приложения «Госуслуги Биометрия».

То есть мы должны связать наше приложение с приложением «Госуслуги Биометрия». Адаптер позволяет сделать это с помощью Intent в Android и URL Scheme в iOS.

Далее определимся с методом встраивания в приложение. Мы остановились на разработке собственного плагина для взаимодействия с хост-платформами с использованием генератора кода Pigeon.

Схема интеграции основана на вызовах методов ЕБС через плагин и получения результата в возвращаемых данных метода.

Итак, мы должны:

  1. Создать плагин и внедрить Pigeon;

  2. В плагин внедрить библиотеку ЕБС Адаптер;

  3. Внедрить плагин в мобильное приложение.

Часть 2. Создание плагина и подключение pigeon

Создаём пустой плагин и в зависимостях dev_dependencies указываем pigeon .

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
  build_runner: ^2.4.8
  pigeon: ^17.2.0

Добавляем в папку lib новый файл - native_api.dart , и создаем абстрактный класс, который будет представлять интерфейс для взаимодействия с нативным кодом. Этот абстрактный класс аннотируется @HostApi(), сигнализируя Pigeon, что он предназначен для использования на хост-платформе. Внутри этого класса определим методы, которые в дальнейшем передадим нативной стороне.

@HostApi()
abstract class NativeHostApi {}

Дополнительно добавим аннотацию @ConfigurePigeon(), которая определит базовую конфигурацию генератора и место хранения сгенерированных файлов.

@ConfigurePigeon(
  PigeonOptions(
	  // Например: lib/native_api.g.dart
    input: '<путь к native_api.dart>',
    dartOut: '<путь к сгенерированному lib/native_api.g.dart>',
    // Например: ios/Classes/NativeApi.g.swift
    swiftOut: '<путь к сгенерированному NativeApi.g.swift>',
    // Например: android/src/main/java/ru/example/app/NativeApi.java
    javaOut: '<путь к сгенерированному NativeApi.java>',
    javaOptions: JavaOptions(
	    // Например: 'ru.example.app'
      package: '<id package>',
    ),
    // Например: ru.example.app
    dartPackageName: '<Имя пакета, в котором будут использоваться файлы pigeon>',
  ),
)
@HostApi()
abstract class NativeHostApi {}

Далее воспользуемся командой генерации type-safe интерфейса для нативных платформ:

flutter pub run pigeon --input lib/native_api.dart

После выполнения данных шагов в нашем проекте (плагине) появятся сгенерированные файлы в папке lib, а также в папках android и ios .

Теперь подключим наши сгенерированные файлы к платформенному каналу. Для этого в файл method_channel.dart добавим:

final NativeHostApi _native = NativeHostApi();

И всё, что нам останется делать на стороне Dart, - это просто вызывать методы из переменной _native, которые будут генерироваться согласно интерфейсу в классе NativeHostApi.

Но это ещё далеко не конец - теперь нам нужно определить наш pigeon-channel в каждой из нативных платформ.

Стоит заранее уточнить, что Plugin в текущей модификации - это класс нашего плагина, который реализует интерфейс FlutterPlugin.

Android

Для версии Андроид перейдем в класс плагина, который реализует интерфейс FlutterPlugin. Обычно он находится по пути android/src/main/<ID Пакета>/<Имя плагина>.java . В этом классе нам нужно реализовать наш сгенерированный интерфейс для нативной платформы. Пример:

public class Plugin implements FlutterPlugin, NativeApi.NativeHostApi {}

В методе onAttachedToEngine инициализируем наш pigeon-channel с помощью setUp :

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "plugin");
        NativeApi.NativeHostApi.setUp(flutterPluginBinding.getBinaryMessenger(), this);
    }

iOS

Ищем метод register и модифицируем его:

public static func register(with registrar: FlutterPluginRegistrar) {
    let messenger : FlutterBinaryMessenger = registrar.messenger()
    let api : NativeHostApi & NSObjectProtocol = Plugin.init()
    NativeHostApiSetup.setUp(binaryMessenger: messenger, api: api)
}

Также, как и с Android, реализуем наш новый Pigeon-интерфейс:

public class Plugin: NSObject, FlutterPlugin, NativeHostApi {}

В процессе добавления новых методов в Dart-интерфейсе, после последующей генерации кода, соответствующая платформа (iOS или Android) попросит добавить стабы для новых или измененных интерфейсов. Это существенно упростит дальнейшую разработку плагина.

Часть 3. Интеграция ЕБС-Адаптера

ЕБС-Адаптер представляет собой .aar файл на Android и .framework для iOS, которые называются ЕБС.Sdk.Adapter. Сама же библиотека лишь выполняет роль моста между нашим приложением и приложением «Госуслуги Биометрия». Библиотека предоставляет метод для старта сессии, авторизации и проверок на возможность авторизации.

Описание библиотеки для Android платформы

Для работы с библиотекой и биометрической системой в Android, ЕБС.Sdk.Adapter предоставляет класс EbsApi:

Возвращаемые данные

Метод

Описание

boolean

EbsApi.isInstalledApp(Context)

Метод возвращает, установлено ли приложение нужное для работы адаптера

String

EbsApi.getAppName(Context)

Метод возвращает название приложения, которое необходимо установить для адаптера.

String

EbsApi.getRequestInstallAppText(Context)

Метод возвращает текст запроса на установку приложения необходимого для работы адаптера.

void

EbsApi.requestInstallApp(Context)

Метод открывает Google Play/AppStore в приложении или браузере для установки его пользователем.

boolean

EbsApi.requestVerification(AppCompatActivity, VerificationRequest)

Основной метод, вызывает запрос на прохождение верификации через адаптер в Госуслуги Биометрия в контексте android.app.Activity

VerificationResult

EbsApi.getVerificationResult(Intent)

Метод получает результат верификации через адаптер в Госуслуги Биометрия

Класс VerificationRequest содержит свойства:

  • String infoSystem - идентификатор системы потребителя;

  • String adapterUri - базовый URL доступа к API Адаптера;

  • String sid - идентификатор сессии;

  • String dboKoUri - URL API получения результата верификации ДБО КО (Информационная система дистанционного банковского обслуживания клиентов контрагента);

  • String dboKoPublicUri - публичный URL ДБО КО, на который Адаптер должен перенаправить пользователя в случае успешного завершения процесса;

Для авторизации используется основной метод EbsApi.requestVerification. Для его реализации требуется разрешение ru.rtlabs.mobile.ebs.permission.VERIFICATION, которое необходимо определить в манифесте. Сам метод умеет работать в двух режимах:

  • Автоматический (VerificationRequestMode.AUTOMATIC) - Адаптер автоматически проверяет наличие установленного приложения «Госуслуги Биометрия». Если оно установлено, то запускается процесс верификации. Если не установлено, выводится встроенное диалоговое окно с запросом на установку «Госуслуги Биометрия»;

  • Ручной (VerificationRequestMode.CUSTOM) - Адаптер не проверяет наличие установленного приложения «Госуслуги Биометрия». В случае отсутствия приложения вернется false. В этом режиме нужно самостоятельно проверять установку «Госуслуги Биометрия» (с помощью EbsApi.isInstalledApp(Context)).

По умолчанию адаптер использует режим VerificationRequestMode.AUTOMATIC .

Рассмотрим каждый из методов взаимодействия:

VerificationRequestMode.AUTOMATIC

На схеме наше мобильное приложение формирует объект класса VerificationRequest для запроса на верификацию. Далее этот объект мы передаем в метод EbsApi.requestVerification. Для выполнения метода должно быть предоставлено разрешение ru.rtlabs.mobile.ebs.permission.VERIFICATION.

Затем с помощью Intent мы переходим в мобильное приложение «Госуслуги Биометрия». В приложении проходим весь необходимый процесс авторизации и получаем результат верификации в методе onActivityResult (EbsApi.getVerificationResult).

VerificationRequestMode.CUSTOM

В ручном режиме все немного сложнее. Сначала мы сами проверяем, установлено ли мобильное приложение «Госуслуги Биометрия» (это можно сделать вызвав метод EbsApi.isInstalledApp). Если приложение не установлено, то мы не можем продолжить верификацию, но можем попросить пользователя установить это приложение при помощи метода EbsApi.requestInstallApp. Далее, как и в первом режиме работы, формируем объект класса VerificationResult и выполняем запрос на верификацию с помощью метода EbsApi.requestVerification . Обязательно перед вызовом, как и раннее, проверяем, предоставлено ли разрешение ru.rtlabs.mobile.ebs.permission.VERIFICATION.

Результат в обоих режимах нам необходимо обработать, проверив состояние результата верификации с помощью свойства verificationResult.state . Разберём каждое свойство:

  • SUCCESS - процесс верификации прошел успешно, и наше мобильное приложение может получить секретный ключ в свойстве verificationResult.resSecret;

  • CANCEL - процесс верификации был остановлен пользователем или системой;

  • REPEAT - процесс был пройден неуспешно, и пользователь запросил повтор на верификацию;

  • FAILURE - процесс верификации завершился с ошибкой.

Описание библиотеки для iOS платформы

На iOS платформе библиотека работает немного по-другому. Для авторизации в ЕБС, библиотека предоставляет класс EbsSDKClient. Разберём, какие методы нам доступны:

Метод

Описание

set (scheme: String, title: String, infoSystem: String)

Метод отправляет и инициализирует процесс верификации через МП «Госуслуги Биометрия».

requestEbsVerification(sessionDetails: EbsSessionDetails, completion: @ escaping RequestEBSVerificationCompletion)

Метод инициализирует процесс верификации через МП «Госуслуги Биометрия». Результат метода приходит в блок completion. В случае успеха параметр не содержит ошибку и содержит resSecret.

openEbsInAppStore()

Метод позволяет открыть МП «Госуслуги Биометрия» страницу в AppStore.

ebsAppIsInstalled()

Метод возвращает свойство, установлено ли МП «Госуслуги Биометрия» на устройстве.

process(openUrl: URL, options: [UIApplication.OpenURLOptionsKey: Any])

Метод обрабатывает открытие нашего приложения из МП «Госуслуги Биометрия» (вызывается из AppDelegate)

Если приложение «Госуслуги Биометрия» установлено, то после вызова метода requestEBSVerification производится авторизация пользователя в ЕСИА. МП «Госуслуги Биометрия» выполняет процедуру получения биометрических образцов, которые передаёт в ЕБС для биометрической верификации. В результате верификации наше приложение получает модель EbsVerificationDetails с полем resSecret, которое мы в дальнейшем обрабатываем на нашем бэкенде.

В процессе верификации могут вернуться разные состояния, это позволит контролировать появление ошибок:

  • .success - удаленная верификация прошла успешно (можно получить verificationResult);

  • .cancel - удаленная верификация была отменена;

  • .failure - удаленная верификация завершена с ошибкой (можно получить объект ошибки); Ошибка детализируется по следующим состояниям:

  • .unknown - неизвестная ошибка;

  • .ebsNotInstalled - МП «Госуслуги Биометрия» не установлено;

  • .sdkIsNotConfigured - SDK не было раннее сконфигурировано (не вызван метод .set(…));

  • .verificationFailed - удаленная верификация пройдена не успешно.

Внедрение бинарного дистрибутива

В папке android создадим директорию libs и добавим туда .aar файл с адаптером. В файле android/build.gradle добавим новую зависимость

implementation(name: '<Название файла>', ext: 'aar')

В этом же файле укажем нашу директорию lib как импровизированный репозиторий Maven с помощью flatDir . Это позволит нам не добавлять .aar файлы в наш основной проект, а хранить их в проекте плагина.

rootProject.allprojects {
    repositories {
        google()
        mavenCentral()
        flatDir {
            dirs project(':<Название плагина>').file('libs')
        }
    }
}

В папку iOS добавим .xcframework. Далее нам нужно определить путь до заранее скомпилированного фреймворка (нашего ЕБС-адаптера). Для этого воспользуемся файлом .podspec, который находится в папке ios. Всё, что нам нужно добавить - это три пункта конфигурации:

  /// Указываем, что все папки внутри фреймворка сохраняются
  /// при установке пода
  s.preserve_paths = 'Frameworks/EbsSDKAdapter.xcframework/**/*'
  /// Указываем компилятору использовать адаптер как дополнительный фреймворк
  s.xcconfig = { 'OTHER_FLAGS' => '-xframework EbsSDKAdapter' }
  /// Указываем ЕБС-адаптер фреймворк для включения в проект при установке пода
  s.vendored_frameworks = 'Frameworks/EbsSDKAdapter.xcframework'

Методы интерфейса для взаимодействия с методами плагина

В прошлом этапе мы написали интерфейс NativeHostApi. Теперь опишем его методы в соответствии с теми, которые предоставляет ЕБС-Адаптер :

abstract class NativeHostApi {
  @async
  bool isInstalledApp();

  @async
  String getAppName();

  @async
  String getRequestInstallAppText();

  @async
  bool requestInstallApp();

  @async
  EbsResultData requestVerification({
    required String infoSystem,
    required String adapterUri,
    required String sid,
    required String dboKoUri,
    required String dbkKoPublicUri,
  });
}

В этом же файле создадим класс EbsResultData, который будет возвращаться при успешном выполнении аутентификации. Пример:

class EbsResultData {
  EbsResultData(
    this.isError,
    this.secret,
    this.errorString,
  );

  final String? secret;
  final String? errorString;
  final bool isError;
}

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

Интегрируем интерфейс в нативную платформу

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

Android

    @Override
    public void isInstalledApp(@NonNull NativeApi.Result<Boolean> result) {}

    @Override
    public void getAppName(@NonNull NativeApi.Result<String> result) {}

    @Override
    public void getRequestInstallAppText(@NonNull NativeApi.Result<String> result) {}

    @Override
    public void requestInstallApp(@NonNull NativeApi.Result<Boolean> result) {}

    @Override
    public void requestVerification(
            @NonNull String infoSystem,
            @NonNull String adapterUri,
            @NonNull String sid,
            @NonNull String dboKoUri,
            @NonNull String dbkKoPublicUri,
            @NonNull NativeApi.Result<NativeApi.EbsResultData> result
    ) {} 

iOS

    func getAppName(
        completion: @escaping (Result<String, Error>
    ) -> Void) {}
    
    func getRequestInstallAppText(
        completion: @escaping (Result<String, Error>) -> Void
    ) {}
    
    func requestInstallApp(
        completion: @escaping (Result<Bool, Error>) -> Void
    ) {}
    
    func requestVerification(
        infoSystem: String,
        adapterUri: String,
        sid: String,
        dboKoUri: String,
        dbkKoPublicUri: String,
        completion: @escaping (Result<EbsResultData, Error>) -> Void
    ) {}

На этом шаге наша интеграция практически завершена, осталось лишь описать работу методов в нативных платформах и возвращать результат в виде callback в метод аргумента result/completion . Например:

    @Override
    public void isInstalledApp(@NonNull NativeApi.Result<Boolean> result) {
        try {
            boolean isAppInstalled = EbsApi.isInstalledApp(context);
            result.success(isAppInstalled);
        } catch (Exception e) {
            result.error(
                    new NativeApi.FlutterError(
                            e.getMessage(),
                            e.getLocalizedMessage(),
                            e.getCause()
                    )
            );
        }
    }

Однако возникает вопрос: как выполнить запрос на верификацию? На Android-платформе для этого воспользуемся Intent и новым классом Activity, расширенным с помощью AppCompactActivity, а данные между нашим FlutterActivity и дочерним EbsActivity будем передавать с помощью Extras .

Для iOS-платформы нам нужно зарегистрировать URL-Scheme для возможности перехода в приложение из приложения «Госуслуги Биометрия» и добавить ключ LSApplicationQueriesSchemes со значением ebsgu.

Вернёмся к Android-платформе. В нашем новом Activity укажем следующие свойства:

    // Используется в onRequestPermissionsResult
    int REQUEST_CODE__PERMISSION = 121;
    // Используется в onActivityResult
    int REQUEST_CODE__VERIFICATION = 122;
    // Используется для возврата результата в FlutterActivity
    static int RESULT_CODE_OK = 234;
    // Используется для возврата результата в FlutterActivity
    static int RESULT_CODE_ERROR = 235;
    
		// Описание ошибки
    static final String cause_field = "cause";
    // Секретный токен
    static final String secret_field = "secret";
    
    // Служебные Extras
    static final String input_info_system = "infoSystem";
    static final String input_adapter_uri = "adapterUri";
    static final String input_sid = "sid";
    static final String input_dbo_ko_uri = "dboKoUri";
    static final String input_dbo_ko_public_uri = "dbkKoPublicUri";

Старт верификации начнем в переопределенном методе onCreate, но перед стартом необходимо проверить разрешения на верификацию. Для этого воспользуемся методом checkSelfPermission, куда передадим наименование разрешения. Если разрешения были раннее приняты, начинаем старт верификации. Если разрешения не приняты, запрашиваем их. При запросе разрешений необходимо отловить результат запроса в переопределённом методе onRequestPermissionsResult. Пример:

    @Override
    public void onCreate(Bundle s) {
        int hasPerm = this.checkSelfPermission(EbsApi.PERMISSION__VERIFICATION);
        if (hasPerm == PackageManager.PERMISSION_GRANTED) {
            /// Начинаем старт верификации
        } else {
            this.requestPermissions(new String[]{EbsApi.PERMISSION__VERIFICATION}, REQUEST_CODE__PERMISSION);
        }
        super.onCreate(s);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == REQUEST_CODE__PERMISSION) {
            if(permissions.length != 0 && Objects.equals(permissions[0], EbsApi.PERMISSION__VERIFICATION)) {
                if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    /// Начинаем старт верификации
                } else {
                    /// Ошибка
                }
            } else {
                /// Ошибка
            }
        }
    }

Теперь разберёмся, как начать сам процесс верификации. Создадим новый метод processVerification(), в котором будем инициировать старт верификации. Для старта нам нужно получить Intent, а также создать экземпляр класса VerificationRequest, в который мы передадим наши Extras. Пример:

    private void processVerification() {
        Intent intent = getIntent();
        VerificationRequest request = new VerificationRequest
                .Builder()
                .infoSystem(intent.getStringExtra(input_info_system))
                .adapterUri(intent.getStringExtra(input_adapter_uri))
                .sid(intent.getStringExtra(input_sid))
                .dboKoUri(intent.getStringExtra(input_dbo_ko_uri))
                .dboKoPublicUri(intent.getStringExtra(input_dbo_ko_public_uri))
                .build();
        VerificationRequestMode verificationRequestMode = VerificationRequestMode.AUTOMATIC;
        EbsApi.requestVerification(this, request, REQUEST_CODE__VERIFICATION, verificationRequestMode);
    }

Этот метод добавляем в onCreate. Результат возвращается в onActivityResult , там мы и поймаем наш итоговый результат с помощью метода EbsApi.getVerificationResult.

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if(requestCode == REQUEST_CODE__VERIFICATION && resultCode == Activity.RESULT_OK) {
            VerificationResult result = EbsApi.getVerificationResult(data);
            switch (result.getState()) {
                case SUCCESS: {
                    /// Возвращаем результат
                    break;
                }
                case CANCEL: {
                    /// Операция отменена
                    break;
                }
                case FAILURE: {
                    /// Произошла ошибка
                    break;
                }
                case REPEAT: {
                    /// Произошла ошибка, пожалуйста повторите
                    break;
                }
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

Вернём результат в наш FlutterActivity и оттуда непосредственно результат в наше приложение на Flutter. Для этого в новом EbsActivity добавим два простых метода - processError и processResult. Первый будет возвращать ошибку, второй - результат:

    private void processResult(@NonNull VerificationResult result) {
        if(result.isValid()) {
            Intent data = new Intent();
            data.putExtra(secret_field, result.getSecret());
            setResult(RESULT_CODE_OK, data);
            finish();
        } else {
            processError("Ошибка валидации");
        }
    }
    private void processError(@NonNull String cause) {
        Intent data = new Intent();
        data.putExtra(cause_field, cause);
        setResult(RESULT_CODE_ERROR, data);
        finish();
    }

В первом методе мы принимаем результат и проверяем, валиден ли он (состояние SUCCESS и непустой секретный токен). Во втором методе мы принимаем описание ошибки и возвращаем её. Эти методы мы добавляем в onActivityResult.

Теперь вызовем EbsActivity через плагин. Для этого в переопределённом методе requestVerification добавим Intent, который будет вызывать EbsActivity. В Intent также добавим Extras. Пример:

    @Override
    public void requestVerification(
            @NonNull String infoSystem,
            @NonNull String adapterUri,
            @NonNull String sid,
            @NonNull String dboKoUri,
            @NonNull String dbkKoPublicUri,
            @NonNull NativeApi.Result<NativeApi.EbsResultData> res
    ) {
        Intent intent = new Intent(binding, EbsActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.putExtra(EbsActivity.input_info_system, infoSystem);
        intent.putExtra(EbsActivity.input_adapter_uri, adapterUri);
        intent.putExtra(EbsActivity.input_sid, sid);
        intent.putExtra(EbsActivity.input_dbo_ko_uri, dboKoUri);
        intent.putExtra(EbsActivity.input_dbo_ko_public_uri, dbkKoPublicUri);
        binding.startActivityForResult(intent, REQUEST_CODE);
        result = res;
    }

В методе onActivityResult в плагине будем отлавливать результат работы EbsActivity проверяя requestCode:

    @Override
    public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == REQUEST_CODE) {
            NativeApi.EbsResultData resData = new NativeApi.EbsResultData
                    .Builder()
                    .setIsError(resultCode == EbsActivity.RESULT_CODE_ERROR)
                    .setSecret(data != null && resultCode == EbsActivity.RESULT_CODE_OK ? data.getStringExtra(EbsActivity.secret_field) : null)
                    .setErrorString(data != null && resultCode == EbsActivity.RESULT_CODE_ERROR ? data.getStringExtra(EbsActivity.secret_field) : null)
                    .build();
            if(result != null) {
                result.success(resData);
                result = null;
            }
        }
        return false;
    }

В данном случае мы сравниваем requestCode . Если он равен тому коду, который мы определили в requestVerification, значит это наш случай, и мы можем получить из Intent всю необходимую информацию и вернуть эту информацию с помощью вызова result.success().
Для iOS-платформы всё немного проще. В файле AppDelegate.swift реализуем метод:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
  EbsSDKClient.shared.process(openUrl: url, options: options)
  return true
}

В нашем методе плагина requestVerification сначала вызовем метод set(scheme: String, title: String, infoSystem: String), куда передадим параметры из метода плагина, а также параметр scheme, в который необходимо добавить зарегистрированную URL-Scheme. Далее просто вызываем requestEBSVerification и получаем модель EbsVerificationDetails, которую обрабатываем в соответствии с содержащимися в ней состояниями. В разрабатываемое приложение возвращаем результат с помощью метода completion(.success(<EbsResultData>)), который генерирует Pigeon.

Вывод

Итак, мы с вами рассмотрели, как можно интегрировать систему ЕБС в мобильное приложение на Flutter, используя Pigeon для интеграции фреймворка и создания полноценного плагина. Надеюсь, этот материал будет вам полезен. Удачного вам дня и лёгких интеграций!

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


  1. ADDA16
    25.09.2024 12:27

    позволяющая идентифицировать человека по биометрическим характеристикам: голосу и лицу.

    Голос и лицо - не идентификаторы. Как косвенные пойдут, но не более того. Набор биометрии намного шире и эти параметры одни из самых ничтожных.


  1. DevUnit Автор
    25.09.2024 12:27

    @ADDA16Всё верно, голос и лицо в совокупности - это образец, анализ которого определяет - являетесь ли Вы субъектом биометрических персональных данных. Такой анализ не легко пройти в системе Госуслуг.


    1. ADDA16
      25.09.2024 12:27

      Никак не определяет. И никаким образцом быть не может.

      Как побочка, косвенные свидетельства, в довесок к дактилокарте и документу удостоверяющего личность, разве что. Самостоятельной ценности не имеют никакой. И не могут иметь.