- PVSM.RU - https://www.pvsm.ru -

Автоматически сгенерированные пароли в iOS 12

Если в вашем приложении есть функция регистрации, включающая в себя возможность или необходимость ввода новых имени пользователя и пароля, скорее всего, вас заинтересует нововведение в «iOS 12» [1], которое я хотел бы обрисовать. Это сервис, который придумывает новые пароли для пользователя, автоматически подставляет их в нужные поля и безопасно хранит в «Keychain» [2].

Автоматически-сгенерированные системой пароли являются наиболее стойкими к подбору (будучи случайно-сгенерированными последовательностями символов – с поправкой на настраиваемые ограничения, но об этом позже), избавляют пользователей приложения от необходимости придумывать последовательность самостоятельно и гибко настраиваются под нужды конкретного приложения. Поддержка новой функциональности довольно легко обеспечивается, тем не менее не без особенностей. Но обо всем по порядку.

Права и обязанности

В первую очередь, приложение должно заявить о своем желании пользоваться этим функционалом. В список «Capabilities» [3] соответствующего «Target» [4] необходимо иметь, во-первых, домен в списке «Associated Domains» [5]. Как ни странно, приложение должно иметь «Associated Domain», чтобы иметь возможность пользоваться сгенерированными паролями и хранить их в «Keychain» пользователя (две эти функции взаимосвязаны, и генерация не может быть использована отдельно от хранения).

Если приложение уже поддерживает использование общих учетных записей с вашим сайтом (т.н. «Shared Credentials») [6], то этот шаг уже позади. Также он может быть уже позади, и если приложение поддерживает «Universal Links» [7] или другой механизм обработки внешних «URL» [8]-ссылок.

Так или иначе, после добавления этой совместимости у приложения появится новое «Entitlement» [9].

Помимо этой, более общей, совместимости приложение также должно иметь «Capatibility» «AutoFill Credential Provider» [10] – это дает возможность приложению, при наличии разрешения от пользователя, пользоваться предложенными системой логинами и паролями. Добавление этой совместимости повлечет появление разрешения «AutoFill Credential Provider Entitlement» [11].

К слову, добавление этой и других возможностей доступно только членам «Apple Developer Program» [12].

В используемый приложением «Provisioning Profile» также должны быть включены перечисленные две функции.

Используемые зависимости

Добавление соответствующих совместимостей повлечет за собой появление фреймворка [13] «AuthenticationServices» [14] в списке «Linked Frameworks and Libraries» соответствующего «таргета». Этот момент обладает некоторыми особенностями, которые стоит упомянуть.

Во-первых, автоматическое добавление связанного фреймворка может не «сработать» с первого раза: при запуске приложения на реальном устройстве из моего экземпляра «Xcode» [15] версии 10.1, приложение сразу «падало» ввиду отсутствия «AuthenticationServices». Ручное удаление фреймворка и добавление его обратно в список связанных компонентов решило проблему.

Во-вторых, автоматическое добавление фреймворка помечает его как «Required». Если ваше приложение подразумевает возможность работы «под» «iOS» версий ниже 12, это также вызовет его падение на этапе запуска «из-под» операционных систем более низких версий. «AuthenticationServices» доступны только для систем версий от 12-ой. Эта проблема решается отмечанием фреймворка как «Optional» [16].

Поддержка в текстовых полях

Для поддержки функционала текстовыми полями используется переменная textContentType [17] протокола UITextInputTraits [18]. Класс UITextField [19], который, скорее всего, используется для ввода логина и пароля в приложении, уже реализует требования нужного нам протокола.

textContentType – это поле типа UITextContentType [20], содержащего лишь набор констант. Нужная нам в данный момент – newPassword [21], используемая для ввода нового, придумываемого в данный момент, пароля (не путать с просто password [22], используемой для ввода уже существующего пароля).

let passwordTextField = UITextField()

if #available(iOS 12, *) {
    passwordTextField.textContentType = .newPassword
}

Установка значения textContentType обернуто в проверку доступности «API» [23], потому что, как и общий функционал, эта константа доступна только начиная с «iOS 12».

Помимо типа содержимого, текстовое поле обязательно должно обеспечивать безопасный ввод данных:

passwordTextField.isSecureTextEntry = true

Хотя приложение вполне может обеспечивать популярный в наше время функционал отображения и скрытия введенного пароля, сгенерированный пароль будет предложен, только если в этот момент флаг имеет значение true [24].

С типом содержимого текстового поля связан интересный момент: если на экране не будет присутствовать другое поле, с типом содержимого username [25], автоматически-сгенерированный пароль предложен не будет. Это связано с тем, что функционал основан не просто на указанном типе контента текстоваого поля, а на эвристическом анализе содержимого экрана [26].

Кажется, генерация пароля «ломает» логику экранов, которые требуют ввести для проверки новый пароль дважды. По-крайней мере, я пока не нашел способ использовать эти две функциональности совместно. И, похоже, я такой не один [27].

Стоит упомянуть, что если логин семантически является адресом электронной почты (и, следовательно, очень хочется иметь соответствующий вид клавиатуры), типы клавиатуры [28] и содержимого допустимо сочетать:

let userNameTextField = UITextField()
userNameTextField.keyboardType = .emailAddress
userNameTextField.textContentType = .username

Требования к паролям

Часто пользовательские пароли должны соответствовать некоторым правилам (иметь определенную длину, включать в себя определенные символы и т.д.). Эти правила можно указать для того, чтобы система учитывала их при генерации паролей. Это делается через свойство passwordRules [29] протокола UITextInputTraits. Например:

if #available(iOS 12, *) {
    passwordTextField.passwordRules = UITextInputPasswordRules(descriptor: "required: upper; required: lower; required: digit; minlength: 8;")
}

Свойство также доступно только начиная с «iOS 12».

Тип свойства – UITextInputPasswordRules [30]. Инициализация – с помощью строки-дескриптора. Дескриптор имеет несложный синтаксис и состоит из простых требований к паролю, перечисленных через точку с запятой. Каждое требование – это пара «ключ-значение», разделенные двоеточием. Ключ представляет собой тип правила (например, «обязательно включает» – required), а значение – элемент, который должен следовать этому правилу (например, цифры – digit).

В примере выше дескриптор означает:

  • required: upper – необходимо присутствие хотя бы одной прописной буквы;
  • required: lower – то же для хотя бы одной строчной буквы;
  • required: digit – то же для хотя бы одной строчной цифры;
  • minlength: 8 – минимальная длина – восемь символов.

Подробное перечисление возможных ключей и значений можно найти в неплохой статье, опубликованной на сайте «NSHipster» [31].

А «Apple» [32] предлагает довольно удобного помощника по составлению дескрипторов [33], который предоставляет не только удобный способ их конструирования, но и проверку составленных дескрипторов в виде неограниченного количества сгенерированных примеров. Там же можно посмотреть, какие правила применяются по-умолчанию.

Валидация

На всякий случай следует уточнить, что механизм генерации паролей не обеспечивает валидацию введенных пользователем данных – об этом необходимо позаботиться самостоятельно. Что, конечно, вполне логично, т.к. пользователь приложения может отказаться от предложенного автоматически-сгенерированного пароля или даже запретить генерацию паролей и автозаполнение полей.

Interface Builder

Что примечательно и в духе нашего времени, все перечисленные настройки текстовых полей можно выставить в «Interface Builder» [34], вплоть до «Password Rule»:

image

Проверка функциональности

Функционал не сложный, но обладает рядом нюансов: при его настройке легко можно что-нибудь забыть. В этом случае в «debug»-сборках при активации соответствующего текстового поля в консоль будет выведена причина, по которой функциональность в данный момент не действует.

Например:

[AutoFill] Cannot show Automatic Strong Passwords for app bundleID: <...> due to error: Cannot save passwords for this app. Make sure you have set up Associated Domains for your app and AutoFill Passwords is enabled in Settings

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

1. iCloud Keychain;
2. AutoFill.

Заключение

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

Особенность довольно интересная и при правильном ее использовании вполне способна улучшить пользовательский опыт [35] вашего приложения!

Автор: Никита Лазарев-Зубов

Источник [36]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ios-development/307451

Ссылки в тексте:

[1] «iOS 12»: https://apple.com/ru/ios/ios-12/

[2] «Keychain»: https://developer.apple.com/documentation/security/keychain_services

[3] «Capabilities»: https://help.apple.com/xcode/mac/current/#/dev88ff319e7

[4] «Target»: https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Targets.html

[5] «Associated Domains»: https://developer.apple.com/documentation/security/password_autofill/setting_up_an_app_s_associated_domains

[6] использование общих учетных записей с вашим сайтом (т.н. «Shared Credentials»): https://developer.apple.com/documentation/security/shared_web_credentials

[7] «Universal Links»: https://developer.apple.com/ios/universal-links/

[8] «URL»: https://en.wikipedia.org/wiki/URL

[9] «Entitlement»: https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html

[10] «AutoFill Credential Provider»: https://help.apple.com/developer-account/#/deva6b71b1d0

[11] «AutoFill Credential Provider Entitlement»: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_authentication-services_autofill-credential-provider

[12] «Apple Developer Program»: https://developer.apple.com/support/app-capabilities/

[13] фреймворка: https://en.wikipedia.org/wiki/Software_framework

[14] «AuthenticationServices»: https://developer.apple.com/documentation/authenticationservices

[15] «Xcode»: https://developer.apple.com/xcode/

[16] «Optional»: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html

[17] textContentType: https://developer.apple.com/documentation/uikit/uitextinputtraits/1649656-textcontenttype

[18] UITextInputTraits: https://developer.apple.com/documentation/uikit/uitextinputtraits

[19] UITextField: https://developer.apple.com/documentation/uikit/uitextfield

[20] UITextContentType: https://developer.apple.com/documentation/uikit/uitextcontenttype

[21] newPassword: https://developer.apple.com/documentation/uikit/uitextcontenttype/2980929-newpassword

[22] password: https://developer.apple.com/documentation/uikit/uitextcontenttype/2865813-password

[23] «API»: https://en.wikipedia.org/wiki/Application_programming_interface

[24] true: https://developer.apple.com/documentation/swift/true

[25] username: https://developer.apple.com/documentation/uikit/uitextcontenttype/2866088-username

[26] эвристическом анализе содержимого экрана: https://developer.apple.com/documentation/security/password_autofill/enabling_password_autofill_on_a_text_input_view

[27] я такой не один: https://stackoverflow.com/questions/52850197/ios-12-swift-strong-password-autofill-doesnt-work-as-expected

[28] типы клавиатуры: https://developer.apple.com/documentation/uikit/uitextinputtraits/1624457-keyboardtype

[29] passwordRules: https://developer.apple.com/documentation/uikit/uitextinputtraits/2980934-passwordrules

[30] UITextInputPasswordRules: https://developer.apple.com/documentation/uikit/uitextinputpasswordrules

[31] неплохой статье, опубликованной на сайте «NSHipster»: https://nshipster.com/uitextinputpasswordrules/

[32] «Apple»: https://apple.com/

[33] помощника по составлению дескрипторов: https://developer.apple.com/password-rules/

[34] «Interface Builder»: https://developer.apple.com/xcode/interface-builder/

[35] пользовательский опыт: https://en.wikipedia.org/wiki/User_experience

[36] Источник: https://habr.com/ru/post/438580/?utm_source=habrahabr&utm_medium=rss&utm_campaign=438580