Единый реестр российских программ и GPL. Мои пять копеек

Несколько дней назад на Хабре была опубликована статья вызвавшая определенный резонанс. Краткий пересказ статьи привело издание CNews, а оттуда тезис о запрете GPL и MPL в Едином реестре перекочевал в статью в Википедии.

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

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

Факт 1. Методические рекомендации (кстати говоря, на сайте ЦКИТ названы «Методические рекомендации по подготовке заявок на включение ПО в Единый реестр», а в протоколе, которым они утверждены — «Методические рекомендации по экспертизе заявок о включении сведений о ПО в Единый Реестр») были утверждены 26 декабря 2019 года, а уже 10 января 2020 года выходит приказ Минкомсвязи № 4, которым в Единый реестр включается Операционная система общего назначения «Стрелец».

Ну и что такого? Да ничего, просто в Технических условиях ОС «Стрелец» прямо написано, что «изделие разработано, как ОС семейства Debian Linux, функционирующее на аппаратной платформе с процессорной архитектурой x86-64.» Технические условия, между тем, лежат на странице, ссылка на которую приведена на странице ОС в Едином реестре.

Включим фантазию и перед нами появятся два варианта: эксперт, проверявший заявку, ознакомился с документами и пустил ПО в Реестр, либо не ознакомился с документацией и не узнал в лицо «ОС семейства Debian Linux»…

Факт 2. Опять немного пробежимся по Реестру и найдем в нем операционную систему общего назначения «ОСнова» включенную в Реестр 19 ноября 2019 года. Официальные документы ОСнова в лице Руководства оператора не скрывают, что основа ОСНовы — Linux. Можно ли это списать на то, что ПО внесено в Реестр еще до утверждения Рекомендаций?

Факт 3. Символично, но под номером 1 в Реестр занесена Система управления базами данных «Ред База Данных», которая гордится тем, что ее ядро построено на основе Firebird. А Firebird, между прочим, выпускается под MPL. Куда ни ткни везде эти GPL да MPL. Подозрительно.

Факт 4. Крайне интересный экземпляр — Операционная система Лотос (редакция для серверов и рабочих станций). Интересна эта ОС тем, что на ее странице в Реестре в сведениях об исключительном праве приведен вот такой текст:

В спойлере производитель ОС Лотос раскрывает мысль, что ограничительные положения свободных лицензии типа GPL применимы только к производному в результате переработки произведению, за исключением разновидности переработки — адаптации

Обоснование наличия исключительного права на дистрибутив операционной системы Лотос Наличие исключительного права Общества с ограниченной ответственностью «Энстрим» (ИНН 7453207581) на дистрибутив операционной системы Лотос (Свидетельство о государственной регистрации № 2017615681, дата государственной регистрации в Реестре программ для ЭВМ 22 мая 2017 г.) основано на нижеследующих положениях Законодательства РФ и международного права.

Принимая во внимание, известную позицию по поводу того, что модификация и разработка собственных производных продуктов, созданных на основе распространяемых согласно ст. 1286.1. ГК РФ на условиях открытой лицензии типа GPL программ для ЭВМ, не влечет получения пользователем программы исключительных прав на программный продукт, создаваемый путем доработки исходного кода. Заявляем Вам, о наличии исключительного права Общества с ограниченной ответственностью «Энстрим» (далее ООО «Энстрим») на дистрибутив операционной системы Лотос (далее ОС Лотос) как составное произведение в соответствии с ч. 2 ст. 1260 ГК РФ.
Согласно ч. 3 ст. 1260 ГК РФ составитель либо иной автор производного или составного произведения осуществляет свои авторские права при условии соблюдения прав авторов произведений, использованных для создания составного произведения. Согласно представленной статье ГК РФ, производные и составные произведение не отождествляются. Из представленной статьи также следует, что Российское законодательство не предполагает согласования с авторами использованных произведений вопроса создания составного произведения, условием является соблюдение их прав.

В гражданском кодексе в ч. 2 ст. 1270 ГК РФ не содержит соответствующее имущественное правомочия (способ использования исключительного права) на создание составных произведений. Для использования чужого произведения в собственном составном произведении по российскому праву необходимо решить вопрос только о таких способах использования как воспроизведение и распространение чужих произведений, а также соблюсти личные не имущественные права.

При создании нашего составного произведения ОС Лотос были использованы самостоятельно разработанные нами независимые программы ЭВМ, а также переработанные программы ЭВМ, открытый исходный код которых распространялся на основании свободных лицензий BSD, MIT, GPL и других. Поскольку в России процесс адаптации различных типов свободных лицензий к национальному законодательству до сих пор не завершен, применять их положения следует через соотношение с нормами четвертой части ГК РФ.

Свободные лицензии типа GPL не запрещают воспроизведение и распространение программ ЭВМ, в этом и суть свободных лицензий. Ограничения в «свободных» лицензиях предусмотрены для результатов переработки и составных произведений, они заключаются в том, что дальнейшее распространение результатов переработки, должно осуществляться на тех же условиях, т.е. свободно.

Однако из российского законодательства следует, что в отношении создания составного произведения данные ограничения ничтожны, так как выходят за пределы способов использования исключительного права автора. При заключении лицензионного договора ч. 1 и ч. 6 ст. 1235 Лицензиар предоставляет Лицензиату именно право использования результата интеллектуальной деятельности с указанием способов использования. Еще раз подчеркнем, что переработка произведения предусмотрена в п. 9 ч. 2 ст. 1270 ГК РФ в качестве способа использования исключительного права, а создание составного произведения — нет.
Следовательно, ограничительные положения свободных лицензии типа GPL применимы только к производному в результате переработки произведению, за исключением разновидности переработки — адаптации.

Дополнительно в подтверждение вышеизложенным обоснованиям приведем цитату из монографии «Интеллектуальная собственность в современном мире» подготовленной в 2017 году ФГБОУ ВО РГАИС под редакцией д-р юрид. наук, профессора И.А. Близнеца. В параграфе 5.2.1.3. Главы 5 Монографии, канд. юрид. наук Р.А. Евдокимов пишет по поводу «свободных лицензий» следующее: «Если пояснить «свободность» программы терминами, традиционными для ГК РФ, то в тексте лицензионного договора должно быть следующее: отсутствуют ограничения на область применения программы и разрешена модификация, адаптация, переработка программы, а также распространение программы в исходном или модифицированном виде без необходимости уведомлять кого-либо о таких изменениях или распространении. Лицензия должна быть безотзывной. Допускаются определенные условности и формальности, до тех пор, пока они не препятствуют нормальной реализации пользователем предоставленных ему прав».

Законность представленной выше позиции, поддерживается и судебной практикой, так, например, судебными решениями Московского городского суда по делам: № 3-0925/2016 от 28.07.2016г.; №3-0952/2016 от 04.08.2016 г.; № 3-1200/2016 от 15.12.2016 г., по иску АО «НПО РусБИТех» были удовлетворены требования «О защите авторских и (или) смежных прав в информационно-телекоммуникационных сетях, в том числе в сети «Интернет»». Согласно решениям, исковые требования мотивированы тем, что истец является обладателем исключительных прав на программу для ЭВМ. В обосновании своего решения Суд указал лишь, что это право подтверждается копией свидетельства о государственной регистрации программы для ЭВМ № 2009616752 «Операционная система специального назначения «Astra Linux Special Edition» для 64-х разрядной платформы на базе процессорной архитектуры х86-64». При этом дистрибутив Astra Linux основан на Debian (свободное программное обеспечение Linux распространяемое по лицензии GPL GNU, стандартную версию ПО можно скачать с официального сайта и репозитория с исходными текстами.

Судебные решения, которые мы представили не остались незамеченными Интернет сообществом и получили ряд комментарием, например, Управляющий директор компании «Россплатформа», бывший президент и генеральный конструктор ROSA (российского дистрибутива Linux) Владимир Рубанов согласен с решением суда и указал: «Astra Linux, как составной продукт, содержит и открытые, и закрытые компоненты, поэтому требование разработчиков удалить его с торрент-раздач в части закрытых составляющих выглядит логично» …

Дистрибутив операционной системы является, с точки зрения ст. 1261 ГК РФ, программой ЭВМ, но это не исключает того, что ОС Лотос является составным произведением. Пакеты программ, из которых как мы указывали, состоит дистрибутив, содержат самостоятельные элементы, распространяемые под свободными лицензиями.

Вообще говоря, любая операционная система как составное произведение содержит множество компонентов, созданных не одним десятком авторов. В ОС Лотос содержатся, элементы, как собственного авторского труда работников нашей компании, так и распространяемые по открытым лицензиям путем присоединения к соответствующим условиям согласно ст. 1286.1 ГК РФ (разрешительным BSD, MIT и лицензий GPL).

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

В результате создания составного программного продукта нами был существенно расширен функционал ранее существовавших программ, меняя представление пользователя о возможности его использования в лучшую сторону. Все программы объединены в единый внутренне согласованный «механизм», преимущества которого оценены пользователями нашего дистрибутива на практике. Данная схема работы известна в мире и получила название VAR (Value-added reseller).

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

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

Наша ОС Лотос — это самостоятельный результат творческого труда (п. 2. ч. 2 ст. 1259 ГК РФ), в составе которого следует представить следующие элементы творческого характера:

  1. общая подборка состава пакетов дистрибутива;
  2. наши авторские программы и библиотеки, компоновка программ, вошедших в каждый из пакетов;
  3. функциональные изменения в настройке и подключении пакетов программ ЭВМ, связанные с удобством пользователя при адаптации ОС Лотос, а также работе с ней;
  4. наличие собственных, юридически чистых программ ЭВМ, когда наше исключительное право обосновано следующим: модифицированные нами программы, на основании свободных лицензий BSD, MIT разрешающих приобретение исключительного права на результат переработки; независимые программы, самостоятельно созданные нашими сотрудниками как служебные произведения;
  5. уникальная среда разработки, состоящая из двух частей: в составе дистрибутива популярные IDE интегрируемые с собственными репозиторием исходного кода и средой непрерывной интеграции; сайт с системой управления репозиториями кода для Git и системой непрерывной интеграции.

Все разработано на основе практики непрерывной интеграции, которая позволяет пользователям сразу начинать пользоваться новыми функциями ОС Лотос, а нам как разработчикам — быстро получать отзывы на эти функции.

Юридическая чистота «Среды разработки», в части сайта, как программы ЭВМ заключается в том, что при ее создании не было использовано ни одного элемента, распространяемого под свободной лицензией GPL. Для функционирования этой важнейшей части системы, были использованы и модифицированы модули, распространяемые под лицензиями BSD и MIT: — организовано хранилище с собственной системой хранения файлов, — программа локализована (переведена на русский язык); — обеспечена возможность для пользователя собрать собственный удобный для него вариант операционной системы, а при обладании специальными познаниями и создать собственные прикладные программы.

На мой взгляд, крайне любопытный текст, больше всего он любопытен тем, что такое обоснование вполне устроило Экспертный совет.

Факт 5. Сами рекомендации советуют: «Подготовьте копии документов, подтверждающих, что исключительное право на заявляемое ПО на территории всего мира и на весь срок действия исключительного права принадлежит вам, например Свидетельство Роспатента». Регистрация программы для ЭВМ осуществляется на основании статьи 1262 ГК РФ. При этом в составе заявки подаются «депонируемые материалы, идентифицирующие программу для ЭВМ», а согласно п. 6 все той же статьи 1262 ГК РФ «Сведения, внесенные в Реестр программ для ЭВМ или в Реестр баз данных, считаются достоверными, поскольку не доказано иное. Ответственность за достоверность предоставленных для государственной регистрации сведений несет заявитель.».

У меня почему-то возникает аналогия между идентифицирующими программу для ЭВМ материалами и ключевыми компонентами ПО, которые «не должны распространяться на условиях таких лицензий, как GPL, MPL». Становится понятнее требование пункта 5 «а» Правил формирования и ведения единого реестра российского ПО в котором требуется «исключительное право на программное обеспечение на территории всего мира и на весь срок действия исключительного права».

Факт 6. В Ведомостях еще в 2017 году обсуждали проблемы российской операционной системы ОСь. Одной из причин невключения ОСи в Реестр было то, что «Ростех» предоставил только универсальную общественную лицензию (GPL) на разработанную операционную систему. Также указывалось на незначительный вклад разработчиков ОСи в ее доработку по сравнению с взятыми за основу CentOS и Fedora.

Подведем итоги. В Реестре есть ПО, содержащее в составе компоненты, лицензируемые по GPL и MPL. Более того, такое ПО включалось в Реестр уже после утверждения Рекомендаций.
Судя по всему, при включении в Реестр анализируется совокупность вклада и лицензирования: если есть существенный вклад и исключительные права на него, то добро пожаловать в Реестр. В связи с этим не могу не согласиться с комментариями к статье, дополнение к которой пишу: комментарий 1, комментарий 2.

Не знаю, действительно ли Рекомендации запрещают любое использование GPL в ПО, подаваемом в Реестр, или вопрос актуален только для операционных систем на основе Linux. Точно так же не знаю, действительно ли эксперты годами включали в Реестр ПО с компонентами под лицензиями GPL и MPL, чтобы потом в один прекрасный момент выпустить рекомендации, на основании которых можно удалить это же ПО из Реестра.

Уверенно можно говорить, о том, что есть неточности, оставляющие поле для трактовок. Остается надеяться, что эти неточности со временем будут устранены.

Наконец, хочется надеяться, что на Хабре стартует неделя статей о Едином реестре и кто-нибудь профессионально осветит вопрос с юридической точки зрения

ссылка на оригинал статьи https://habr.com/ru/post/504312/

USB over IP в домашних условиях

Иногда возникает желание работать с устройством, подключенным по USB, не держа его на столе рядом с ноутбуком. У меня таким устройством является китайский гравёр с лазером на 500 мВт, штука довольно неприятная при близком контакте. Помимо непосредственной опасности для глаз, в процессе работы лазера выделяются токсичные продукты горения, поэтому устройство должно находится в хорошо проветриваемом помещении, и желательно изолированно от людей. А как же таким устройством управлять? Ответ на данный вопрос я случайно нашел, просматривая репозиторий OpenWRT в надежде найти достойное применение старенькому роутеру D-Link DIR-320 A2. Для подключения решил использовать описываемый на Хабре ранее USB over IP tunnel, однако все инструкции по его установке успели потерять актуальность, поэтому пишу свою.

OpenWRT — операционная система, не нуждающаяся в представлении, поэтому её установку расписывать не буду. Для своего роутера взял последний стабильный релиз OpenWrt 19.07.3, и подключил его к основной точке доступа по Wi-Fi в качестве клиента, выбрав режим lan, чтобы не мучать файрвол.

Серверная часть

Действуем согласно официальной инструкции. После подключения по ssh устанавливаем необходимые пакеты.

root@OpenWrt:~# opkg update root@OpenWrt:~# opkg install kmod-usb-ohci usbip-server usbip-client

Далее подключаем к USB-порту роутера наше устройство (в моём случае устройства: USB-хаб, флешку, на которую смонтирована файловая система роутера (ввиду нехватки места на внутреннем накопителе), и, непосредственно, гравёр).

Пробуем вывести список подключенных устройств:

root@OpenWrt:~# usbip list -l

Пусто.

Путём гугления был найден виновник, им оказалась библиотека libudev-fbsd.
Вытаскиваем руками из репозитория последнюю рабочую версию libudev_3.2-1 из релиза OpenWRT 17.01.7 под свою архитектуру, в моём случае это libudev_3.2-1_mipsel_mips32.ipk. С помощью wget/scp загружаем её в память роутера и переустанавливаем

root@OpenWrt:~# opkg remove --force-depends libudev-fbsd root@OpenWrt:~# opkg install libudev_3.2-1_mipsel_mips32.ipk 

Проверяем:

root@OpenWrt:~# usbip list -l  - busid 1-1.1 (090c:1000)    Silicon Motion, Inc. - Taiwan (formerly Feiya Technology Corp.) : Flash Drive (090c:1000)   - busid 1-1.4 (1a86:7523)    QinHeng Electronics : HL-340 USB-Serial adapter (1a86:7523)

Китаец, подключенный в USB-хаб, получил bsuid 1-1.4. Запомнили.

Теперь запускаем демон:

root@OpenWrt:~# usbipd -D

и биндим китайца

root@OpenWrt:~# usbip bind -b 1-1.4 usbip: info: bind device on busid 1-1.4: complete

Проверяем, что всё работает:

root@OpenWrt:/home# netstat -alpt Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name tcp        0      0 0.0.0.0:3240            0.0.0.0:*               LISTEN      1884/usbipd

Чтобы далее биндить девайс автоматически, подредактируем /etc/rc.local, добавив перед exit 0 следующее:

usbipd -D & sleep 1 usbip bind -b 1-1.4

Клиентская часть

Попробуем поключить устройство к Windows 10, используя вышеупомянутую инструкцию с openwrt.org. Сразу скажу: затея обречена на провал. Во-первых, рассматривается только Windows 7 x64. Во-вторых, дана ссылка на тред на sourceforge.net, в котором предлагается скачать с дропбокса патченый в 2014 году драйвер. При попытке запустить его под Windows 10 и подключиться к нашему устройству получаем ошибку:

c:\Utils\usbip>usbip -a 192.168.31.203 1-1.4 usbip for windows ($Id$)  *** ERROR: cannot find device

Связано это с тем, что клиент не работает с сервером, собранным под ядро старше версии 3.14.
Сервер usbip под OpenWRT 19.07.3 собран на ядре 4.14.180.

Продолжая поиски, натыкаюсь на актуальную разработку виндового клиента на github. Ок, заявлена поддержка Windows 10 x64, но клиент исключительно тестовый, поэтому присутствует ряд ограничений.

Итак, сначала просят установить сертификат, притом дважды. Ок, помещаем его в Trusted Root Certification Authority и Trusted Publishers.

Далее необходимо перевести операционную систему в тестовый режим. Делается это командой

bcdedit.exe /set TESTSIGNING ON

С первого раза у меня не получилось, помешал secure boot. Для его отключения необходимо перезагрузиться в UEFI, и выставить secure boot — disable. На некоторых моделях ноутбуков может потребоваться установка supervisor password.

После этого загружаемся в Windows и делаем bcdedit.exe /set TESTSIGNING ON
Винда говорит, что всё ок. Снова перезагружаемся, и видим в правом нижнем углу надпись Test Mode, версию и номер билда ОС.

Для чего же все эти манипуляции? Для установки неподписанного драйвера USB/IP VHCI. Сделать это предлагается, скачав файлы usbip.exe, usbip_vhci.sys, usbip_vhci.inf, usbip_vhci.cer, usbip_vhci.cat, и выполнив с правами администратора

usbip.exe install

либо второй способ, установка Legacy Hardware в ручном режиме. Я выбрал второй вариант, получил предупреждение об установке неподписанного драйвера и согласился с ним.

Далее проверяем, что у нас есть возможность подключиться к удаленному USB-устройству, выполняя команду:

usbip.exe list -r <ip вашего роутера>

получаем список устройств:

c:\Utils\usbip>usbip.exe list -r 192.168.31.203 usbip: error: failed to open usb id database Exportable USB devices ======================  - 192.168.31.203       1-1.4: unknown vendor : unknown product (1a86:7523)            : /sys/devices/ssb0:1/ehci-platform.0/usb1/1-1/1-1.4            : unknown class / unknown subclass / unknown protocol (ff/00/00)

на ошибку usbip: error: failed to open usb id database не обращаем внимания, на работу не влияет.

Теперь биндим устройство:

c:\Utils\usbip>usbip.exe attach -r 192.168.31.203 -b 1-1.4

Всё, винда обнаружила новое устройство, теперь с ним можно работать так, как будто оно физически подключено к ноутбуку.

С китайским гравёром пришлось немного помучаться, так как при попытке установить его драйвер CH341SER через прилагавшийся к гравёру инсталлятор (да, гравёр на Ардуино), USB/IP VHCI ронял винду в BSOD. Однако установка драйвера CH341SER до подключения устройства через usbip.exe решала проблему.

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

Использованные источники:

https://openwrt.org/docs/guide-user/services/usb.iptunnel
https://github.com/cezanne/usbip-win

ссылка на оригинал статьи https://habr.com/ru/post/504338/

Евгений Дикий: «Локально Антарктиду загадили меньше всего, но глобальное загрязнение здесь очень заметно»

Сейчас в DataArt продолжается программа Eco Weeks, запустив которую, мы заинтересовались жизнью Антарктики и полярных станций разных стран. Отправили несколько вопросов в Национальный антарктический научный центр Украины, а в итоге записали большое интервью с его директором Евгением Диким и Еленой Марушевской, которая отвечает за внешние коммуникации центра! О Пластикожуе и компосте для полярной теплицы, советских китобоях и туристических лайнерах, старом добром дизеле, глобальном потеплении и новых экологических исследованиях.

— Сейчас весь мусор из Антарктиды нужно вывозить на другие материки?

Евгений Дикий: В ходе последней пересменки, помимо всего прочего, забрали полный трюм мусора на утилизацию. А вообще новый подход к обращению с отходами входит в программу модернизации станции «Академик Вернадский». Мы сейчас внедряем новые экологические подходы, а заодно выгребаем то, что накопилось за время, когда вложений в станцию не делалось. Возможно, мы в ближайшем будущем станем первой станцией в Антарктиде с собственной переработкой пластика прямо на месте.

Елена Марушевская: У нас есть идея по переработке пластика под кодовым названием «Пластикожуй», которую не удалось реализовать в этом году из-за пандемии и связанных с ней ограничений. Но вообще для проекта, который осуществляется при помощи компании Кока-Кола в Украине, все готово, осталось довести оборудование до станции. Сам Пластикожуй — это такой мультяшный герой, который занимается переработкой пластика. Если точнее — твердого пластика с маркировкой “2” или HDPE, из которого делают крышки и непрозрачные флаконы для бытовой химии. Отходы из этого материала мы собираемся дробить прямо на станции и делать из них сувениры. Все это устроено довольно просто: дробленный пластик засыпается в аппарат, внешне похожий на кофеварку, в котором находятся формочки и откуда выпадает, например, красивый в разводах брелок.


Макет одного из сувениров, которые на украинской станции «Академик Вернадский» планируют производить из твердого пластика, начиная со следующего года

Основной смысл, конечно, в том, чтобы вообще не вывозить пластик на утилизацию, а дать ему новую жизнь тут же на месте. Забрать его уже на память смогут туристы — это сократит объем мусора на станции и позволит им не искать, что бы прихватить с собой на память со станции.

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

Что касается ПЭТ, его мы тоже планируем дробить и в таком виде уже продавать в порту. Т. е. не платить за его утилизацию, как сейчас, а самим получать за него деньги.

Е. Д.: С органическими отходами история тоже по-своему занимательная, но немного грустная. Дело в том, что органику с кухни до недавнего времени утилизировали очень простым способом — скармливали чайкам-поморникам. И птицы привыкли, что в определенное время им достаются объедки. Если в какой-то день повар их вовремя не выбрасывал, один особенно наглый поморник стучал клювом в окно: «Ребята, вы чего, обед!» Но на самом деле, это грубое нарушение — делать так в Антарктиде категорически нельзя, и эту практику мы, конечно, свернули. Мы не имеем права вмешиваться в жизнь местной фауны, прикармливая птиц. Поморников жалко, но им придется отвыкать: протокол об охране окружающей среды не оставляет выбора.


Южнополярный поморник. Фото со страницы Национального антарктического научного центра Украины в Facebook

Но если чаек больше не кормим, то куда девать объедки? Пока самая креативная идея — перерабатывать их на компост в специальных мини-бродилках. Тем более, мы в любом случае собираемся построить на станции нечто вроде небольшой теплички или оранжереи. Это как раз соответствует Договору об Антарктике, при условии, что грунт будет полностью закрытым и никаких семян или удобрений не попадет во внешнюю среду. Первый такой эксперимент провели немецкие коллеги, на их станции «Ноймайер» теплица уже работает. Вполне логично использовать в качестве компоста свою же переработанную органику со станции.

Е. М.: Добавлю, что такие ведра-бродилки мы уже закупили и пока тестируем их у себя дома. Процесс брожения действительно идет, наружу ничего не вылезает, запаха нет. Такие ЭМ-контейнеры с бактериальной культурой внутри довольно широко сейчас используются для компостирования внутри квартиры. Мы и подумали, поскольку станция «Академик Вернадский» — в сущности тоже одна большая квартира, из которой еще и ничего нельзя выносить, они должны там сработать. Температура на станции такая же, как и дома. Но нам еще нужно подготовить место, куда уже готовый грунт высыпать. А что там выращивать, повар уже давно придумал: и лук, и петрушку — свежие витамины!

— Вообще присутствие человека в Антарктиде заметно?

Е. Д.: Человек — такой фактор, который становится заметным везде, где появляется. Но, конечно, Антарктиде повезло гораздо больше, чем любому другому континенту. Начнем с того, что в этом году мы только 200 лет отмечали с открытия континента. Для сравнения, самой последней до Антарктиды заселялась Южная Америка — это примерно 11 000 лет назад. Все остальные континенты к тому моменту уже были плотно заселены. Так что в силу времени — 200 лет против 11 000 — это наименее загаженный человеком континент. Опять же, плотность популяции тут просто прекрасная: в летний период еще вопрос, как туристов считать, но если брать только население станций, летом это до 4000 человек на весь континент, а в зимний период там остается меньше 2000 человек.

Но и в Антарктиде человек тоже успел нагадить, причем очень конкретно нагадить. Особенно активно этим занимались наши с вами земляки — т. е. Советский Союз. В частности, отдельная мрачная страница истории — советские китобойные флотилии. Одна называлась «Слава», другая — «Советская Украина», базировались они в Одессе, ходили в Южный океан каждое лето с 1948 по 1987 годы. И хотя, скажем, японцы и норвежцы очень хорошо приложились к популяции китов, советские китобои нагробили их больше, чем все остальные вместе взятые! И если вы будете когда-то на русской станции «Беллинсгаузен», увидите рядом с ней кучу громадных ржавых топливных баков — здесь была база снабжения китобойных флотилий. Дизель для кораблей там хранился тысячами тонн, а теперь они медленно превращаются в ландшафт, напоминая до сих пор об этой истории.


За время активного промысла в ХХ веке общая численность синих китов в мире сократилась с 215 тысяч (минимальная оценка, называют и другие, вплоть до 350 тысяч) до 11-12 тысяч. Фото: Национальный антарктический научный центр Украины

В чем Антарктиде, надо сказать, бешено повезло — это протокол, который запрещает использование недр, который пока действует до 2048 года (Мадридский протокол). Если мы вспомним, какой процент загрязнения дает именно разработка недр в любой из наших стран, сможем представить, насколько чище там, где вообще ничего добывать нельзя.

По большому счету, что сейчас человек оставляет в Антарктиде? Ну, органические отходы, понятное дело. Тут есть норматив: если население станции не более 30 человек, их разрешается просто сбрасывать в море, без очистки. Если больше, сточные воды уже надо как-то очищать. У нас в этом плане тонкий нюанс: мы как раз на грани. Дело в том, что зимой у нас на «Академике Вернадском» 12 человек, а вот летом бывает, что и чуть больше 30. Когда приезжает сезонный летний отряд, то в принципе, бывает иногда под 40. Поэтому мы задумываемся о том, что канализацию нам тоже пора чистить. Хотя, если бы речь шла только об органике, то по сравнению с пингвинами и тюленями на нашем же острове, это настолько смехотворный объем, что и говорить не о чем. Но, есть одно отличие — тюлени не используют моющие средства и стиральный порошок, а полярники используют! Вот в этом дополнении поверхностно-активных веществ и заключается реальный вред от канализационных сливов.

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


Фото: Национальный антарктический научный центр Украины

Далее есть бытовой мусор. Но сейчас очень строгие нормы, которые требуют его полностью вывозить на утилизацию, и они в целом соблюдается. Раньше была неприятная практика, когда суда, только выйдя за 60-й градус, т. е. за пределы зоны действия Договора об Антарктике, все отходы попросту сбрасывали в море. Восточная Европа точно этим грешила, но сейчас это считается варварством уже абсолютно всеми. Мы сейчас в ходе модернизации станции вывозим уже не только то, что копится за сезон, но и пытаемся разгрести старые завалы. В этом году очень много почистили, забили полный трюм: для сравнения, счет за утилизацию мусора в чилийском порту Пунта-Аренос в этом году — в 3 раза больше, чем в прошлом.

— Что именно успело скопиться? «Академик Вернадский» — в прошлом британская станция?

Е. Д.: Да, это бывшая британская станция «Фарадей», одна из очень старых, она вообще была основана в 1947 году. Правда, с тех пор осталась только точка на карте и один домик, имеющий статус музея, «Вурди Хауз», те же здания которыми мы пользуемся, построены в 1970-х. Кстати, это как раз одна из двух станций, где была открыта озоновая дыра. Вся большая международная движуха по сохранению озонового слоя началась именно с того, что британские физики на «Фарадее» и «Халли» заметили, что с озоновым слоем что-то не то. Мы теперь эту вахту приняли, озон у нас измеряется каждые три часа вне зависимости от погоды.

Возвращаясь к мусору, британцы ничего такого нам не оставили. Хотя кое-что из оборудования, переданного нам в 1990-х, успело за эти годы превратиться в отходы. Но станцию 25 лет назад они передали чистенькую, как куколка. Накопиться успело много старого оборудования — все-таки бытовой мусор вывозили каждый год, а вот тяжелое железо не всегда. В частности, сейчас на утилизацию пошли старые моторные лодки и снегоходы, которые занимали очень много места и не подлежали восстановлению. Кроме такого тяжелого металлического мусора, каждый год собираются десятки бочек с отработкой от дизель-генераторов, и того, что соскребается со стенок при чистке топливного бака. В этом году мы вывозим 60 бочек.


Украинская антарктическая станция «Академик Вернадский» расположена на острове Галиндез Аргентинского архипелага, вблизи Антарктического полуострова

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

Е. Д.: Честно говоря, я вообще считаю день, когда Россия отказала Украине в передаче одной из советских антарктических станций, очень счастливым. Наше правительство обращалось к российскому в 1992-м — просило от одной до двух из 12 станций на основании того, что строили их все-таки все вместе. Но Россия была категорически против, что в итоге обернулось удачей. Да, три года мы промучались без своей станции, зато потом получили британскую, построенную по принципиально иным стандартам. Помните, как в «Хоббите» у Толкина? «Нора была хоббичья, а значит — благоустроенная». Такую хоббичью станцию британцы Украине и отдали.

С учетом того, сколько лет Украина ее эксплуатировала, не вкладываясь в ее ремонт, думаю, советская станция до сегодняшнего дня просто не дожила бы. Бывшему «Фарадею» хватило запаса прочности — собственно модернизацию станции начал только два года назад. Считайте: 23 года с момента передачи, плюс британцы же нам ее отдавали не сразу после ремонта. Реально многим системам было уже лет по 40! Но они выдержали, а дизеля, например, 1980-го года, до сих пор дорабатывают. Вот в следующем году заменим их наконец.


Здания, которые украинская экспедиция использует сейчас, построена британцами в 1970-х

— Глобальные антропогенные изменения в Антарктиде заметны особенно сильно?

Е. Д.: Загадили меньше — это касается только локального загрязнения, глобальное загрязнение здесь очень заметно. Начнем с глобального потепления, от которого страдают, прежде всего, полярные регионы: Антарктика здесь идет сразу после Арктики. Например, на нашей станции за время наблюдений среднегодовая температура выросла на 3⁰ С. Тоже, казалось бы, что такое 3⁰ С? У нас с вами в течение дня температурные колебания больше бывают. Но если мы говорим о среднегодовом значении, то разница получается, примерно как между Киевом и Петербургом. И такое потепление произошло в Антарктиде на глазах одного поколения!

Думаю, вы более или менее регулярно видите в сети сообщения об отколовшихся айсбергах размером с пол-штата Нью-Йорк или, скажем, с пол-Голландии. Но это же гауссиана: если в конце кривой там такие крупные айсберги, то по середке остаются миллионы мелких, которые никто не отслеживает, но они все равно куда-то плывут. В Антарктиде это, кстати не только видно, но и слышно. Там же очень тихо. Если только ты не возле дизеля стоишь, то никаких фоновых шумов, к которым мы привыкли в обычной жизни, просто нет. Т. е. ты слышишь только звук ветра или какое-то отдаленное копошение каких-нибудь божьих тварей. И вдруг посреди этого как пушка: ба-бах! Это очередной кусок льда откололся, упал и уплыл. И такое происходит каждые несколько часов.


Фото: Национальный антарктический научный центр Украины

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

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

Скорее всего, это значит, что их нужно выводить из оборота. На уровне Европейского Союза создано Европейское химическое агентство (ECHA), которое должно рассматривать такие данные и формулировать ограничения для промышленности. У нас, кстати, был уже один успех. Мы параллельно работаем по Черному морю и нашли там один пестицид, который не разлагается, как уверял производитель, а накапливается рыбах и дельфинах. Эта информация стала основанием для того, чтобы этот препарат все-таки запретить, заменив его другим видом.

Е. Д.: Еще один из глобальных загрязнителей, который в Антарктике только начали изучать, но уже на 100 % известно, что он там есть — продукт разрушения всех пластиковых бутылок. К сожалению, большинство видов пластика не разлагаемы биологически, но механически они дробятся. Когда бутылки выносит в океан, они постепенно измельчаются до фракций такого размера, что какой-нибудь рачок не в состоянии отличить кусочки пластика от водорослей, которыми питается. В итоге криль антарктический начинает дохнуть с голода при переполненных желудках, потому что они просто заполнены пластиком вместо нормальной еды. Для Антарктиды это пока только начинает изучаться, а в других районах океана, уже, к сожалению, показано, что проблема серьезная. И опять же, Антарктида при всем своем туризме и полярных станциях и близко не дает такого количество пластика — его однозначно приносит течениями.

— Кстати, туристов в Антарктиде много?

Е. Д.: До коронавирусного кризиса рынок туристических услуг в Антарктиде рос на 15-20 % в год, наверное, с такой скорость может расти разве что IT-сфера. На нашей станции с ее зимним населением в 12 человек за прошлое лето, за туристический сезон, побывало около 5000 туристов. Это значит, что были они и на «Беллинсгаузене», который севернее, т. е. ближе к источнику туризма. Т. е. по сравнению с полярниками туристов очень много. Причем туризм развивается по двум направлениям: экстремальный, на яхтах под парусом, и «классический» — с круизными лайнерами, которые берут и по 200, и по 500 пассажиров. Но все это это касается только одного кусочка — района Антарктического полуострова, поскольку в остальные точки любая поездка уже эксклюзивная и очень дорогая. Вглубь континента добираются единицы, может, иногда десятки человек за год, но не больше.


Hanseatic Inspiration — одно из трех экспедиционных судов серии Hanseatic. Их особенность — большие обзорные палубы, суда активно используются для круизов в Антарктиду. Фото: hl-cruises.com

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

— Энергетика пока полностью дизельная?

Е. Д.: Пока все на дизеле. К большому моему сожалению, так сложилось исторически — на момент, когда станция строилась, альтернативу вообще не рассматривали. Сейчас нам бы хотелось это изменить, но дело оказалось очень непростым. Антарктида достаточно большая и то, что мы себе часто представляем: летом -30, зимой -80, этакий Марс — относится к центральным районам материка. Там, где расположены российская станция «Восток» или американская «Амундсен—Скотт», действительно жуткие температуры, зато почти абсолютная сухость — практически идеальные условия для ветровой энергетики. Почти весь год там равномерные ветра, которые как бы скатываются по ледовому куполу от полюса в сторону побережья. Там есть тренд на установку ветряков. Некоторые станции, построенные в XXI веке, вообще zero-emission, первой такой была бельгийская «Принцесса Элизабет». Новые проекты, которые я видел, тоже рассчитаны на это. Старые станции пока только частично заменяют старый добрый дизель на возобновляемые источники энергии, главный из которых — солнце. Полгода оно, конечно, не работает, но в оставшуюся половину солнечные панели очень даже себя оправдывают.

Но если мы берем наш кусок Антарктиды, так называемую Maritime Antarctic — Морскую Антарктику — то видим гораздо более мягкий климат. Температурный рекорд у нас -46, но вообще обычно зимой мороз держится в районе -30, а летом температура вообще плавает вокруг нуля в диапазоне от -5 до +5. Но с другой стороны, здесь почти стопроцентная влажность и 270 дней в году идет либо снег, либо мокрый снег, либо снег с дождем. Ветра при этом от полного штиля до 40 м/с. А большинство ветряков рассчитано на работу в довольно узком диапазоне. Если ветер усиливается, они просто останавливаются, потому что иначе их сорвет. Поэтому я боюсь, что полный переход на альтернативку нам сделать вообще не удастся. Но сэкономить 20-30 % топлива мы бы очень хотели — это уже было бы вкладом в борьбу с выбросами. Надеемся на солнечные панели, по нашим прикидкам, летом они как раз примерно 30 % экономии способны обеспечить. Но этот проект пока на стадии расчетов.

ссылка на оригинал статьи https://habr.com/ru/company/dataart/blog/504326/

Обратная связь, которую вы не даете, так же значима, как и та, которую даете

Фабрицио Тейшейра — дизайнер в Work & Co, основатель UX Collective.


То, что вы, как дизайнер, НЕ включаете в свои работы, тоже является дизайном – ведь наша работа состоит в приоритизации и сокращении. Так и обратная связь, которую вы НЕ даете, является чрезвычайно важной частью вашей обратной связи по дизайну.


Фото Ricardo Mancía

В последнее время я уделяю много внимания одному аспекту дизайна, который напрямую не связан с ремеслом: созданию безопасной обстановки для совместной работы, где дизайнеры могут работать с максимальной отдачей.

Я видел и слышал много историй о талантливых дизайнерах, которые испытывают досаду из-за внешних факторов, которые в итоге мешают им работать на 100%. Эти факторы могут включать многое: менеджеров, которые не дают дизайнерам достаточно пространства для роста; коллег, которые не дают им должное признание за идеи; излишне соперническую среду; страх подвергнуться критике на публике; синдром самозванца; неясность в отношении следующих шагов.

Что общего во всех этих историях?

Дизайнеры, самооценка которых была подорвана влиянием извне.

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

Очевидность

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

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

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

С другой стороны, ваше руководящее положение не дает вам никакого права быть м***ком. Вам не нужно быть Капитаном Очевидность на каждом собрании.

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


Фото Solal Ohayon

#1 Чтобы внести коррективы, нет необходимости доказывать, что другие неправы

Распространенная техника переговоров при попытке убедить кого-то в ваших собственных идеях заключается в нападках на все, что отличается от вашего видения.

Просто не надо.

Осуждать дизайн, просто чтобы одержать верх – это ребячество и только покажет остальным членам команды вашу незрелость. Даже сама мысль, что нужно сначала ~победить~ одну идею, чтобы продать другую, является признаком того, что вы создали для своей команды чрезмерно конкурентную среду. Токсичная среда вызывает у людей желание уйти.

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

#2 Не нужно использовать обратную связь, чтобы демонстрировать другим, сколько вы знаете

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

Руководители/директора, которые так делают, обычно не очень уверены в себе, или все еще пытаются доказать свою значимость этим людям. Проблема в том, что переключение внимания на себя вместо обратной связи, приведет к потере нескольких дорогих минут, которые можно было бы посвятить обсуждению того, как улучшить дизайн. Кроме того, превознося свои сильные стороны, вы можете вызывать чувство неуверенности у окружающих.

В конечном счете, каждое обсуждение на разборе дизайна должно быть посвящено тому, как улучшить дизайн. А не вашему блестящему прошлому.

#3 Надо уметь отделять концептуальную обратную связь от детальной обратной связи

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

До момента, пока кто-нибудь не выскажет этот один комментарий.

«Я не знаю… Я бы использовал подчеркивание на ссылках, чтобы было понятнее, что пользователь может по ним кликнуть».

«Я не знаю… Я думаю, нам стоит спрятать статусную строку телефона, когда пользователь смотрит видео в полноэкранном режиме».

«Я не знаю… Я не уверен, что этот цветовой контраст пройдет высшие критерии по доступности».

Серьезно?

Вы серьезно решили, что первая неделя проекта – это подходящее время для обратной связи подобного рода?

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

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

#4 Можно достичь тех же результатов, направив внимание на то, что работает, а не то, что не работает

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

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

#5 Отрицательная обратная связь заразна и влияет на моральный дух команды

Мы все видели такие собрания по разбору дизайна, на которых дизайнеры, получив много обратной связи, в итоге чувствуют себя совсем потерянными. В помещении практически видно темное облако после разборов, вышедших из-под контроля.

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

#6 Некоторые комментарии лучше делать в частном формате, чем в публичном

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

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

Кроме того, вы не хотите, чтобы дизайнер смутился перед большой группой из-за простой опечатки или несогласованности в дизайне.

Быстрое отправленное вдогонку в Slack сообщение вроде “кстати, я увидел несколько опечаток на главной странице, которую вы показывали ранее", может быть гораздо эффективнее.

#7 В конечном счете, все это о том, чтобы раскрывать в людях лучшее

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

Обратная связь, которую вы не даете, так же значима, как и та, которую даете.

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

ссылка на оригинал статьи https://habr.com/ru/post/504342/

Аутентификация — CUSTOM SETUP / AWS Amplify + React Native

cognito

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

Аутентификация

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

Авторизация

это проверка и определение полномочий на выполнение некоторых действий в соответствии с ранее выполненной аутентификацией

В конце этой статьи, мы с вами сделаем это мобильное приложение:

cognito

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

Большинству приложений требуются механизмы для регистрации пользователей, их входа в систему, обработки шифрования и обновления паролей, а также множества других задач, связанных с управлением идентификацией. Современные приложения часто требуют таких вещей, как OAUTH (открытая аутентификация), MFA (многофакторная аутентификация) и TOTP (основанные на времени пароли времени).

В прошлом разработчикам приходилось вручную раскручивать все эти функции аутентификации с нуля. Одна только эта задача может занять у команды разработчиков недели или даже месяцы, чтобы сделать все правильно и сделать это безопасно. К счастью, сегодня есть полностью управляемые сервисы аутентификации, такие как Auth0, Okta и Amazon Cognito, которые обрабатывают все это для нас.

В этой статье вы узнаете, как правильно и безопасно внедрить аутентификацию в приложении React Native с использованием Amazon Cognito с AWS Amplify.

Amazon Cognito

это полностью управляемый сервис идентификации от AWS. Cognito обеспечивает простую и безопасную регистрацию пользователей, вход в систему, контроль доступа, обновление токенов и управление идентификацией пользователей. Cognito масштабируется до миллионов пользователей, а также поддерживает вход в систему с поставщиками социальных сетей, такими как Facebook, Google и Amazon.

Cognito состоит из двух основных частей: пулов пользователей и пулов идентификации.

User Pools

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

Identity pools

пулы удостоверений позволяют вам авторизовать пользователей, вошедших в ваше приложение, для доступа к различным другим сервисам AWS. Допустим, вы хотите предоставить пользователю доступ к лямбда-функции, чтобы он мог получать данные из другого API. Вы можете указать это при создании пула удостоверений. В пулы пользователей входит то, что источником этих идентификаторов может быть пул пользователей Cognito или даже Facebook или Google.

Сценарий, когда пул пользователей Amazon Cognito и пул удостоверений используются вместе.

Смотрите схему для общего сценария Amazon Cognito. Здесь цель состоит в том, чтобы аутентифицировать вашего пользователя, а затем предоставить ему доступ к другому сервису AWS.

cognito

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

  2. Затем ваше приложение обменивает токены пула пользователей на учетные данные AWS через пул удостоверений.

  3. Наконец, пользователь вашего приложения может затем использовать эти учетные данные AWS для доступа к другим сервисам AWS, таким как Amazon S3 или DynamoDB.

Cognito User Pools позволяет вашему приложению вызывать различные методы для службы для управления всеми аспектами идентификации пользователя, включая такие вещи, как:

  • Регистрация пользователя
  • Вход в систему пользователя
  • Выход пользователя
  • Смена пароля пользователя
  • Сброс пароля пользователя
  • Подтверждение кода MFA
  • Интеграция Amazon Cognito с AWS Amplify

AWS Amplify поддерживает Amazon Cognito различными способами. Прежде всего вы можете создавать и настраивать сервисы Amazon Cognito непосредственно из интерфейса командной строки AWS Amplify. Создав службу аутентификации через CLI, вы можете вызывать различные методы (например, signUp, signIn и signOut) из приложения JavaScript с помощью клиентской библиотеки Amplify JavaScript.

Amplify также имеет предварительно настроенные компоненты пользовательского интерфейса, которые позволяют выстраивать целые потоки аутентификации всего за пару строк кода для таких сред, как React, React Native, Vue и Angular.

Вы спросите и сколько же это все стоит?

Платите только за то, чем пользуетесь. Никаких минимальных платежей.

Используя Amazon Cognito Identity для создания пула пользователей, вы платите только за количество активных пользователей в месяц (MAU). MAU — это пользователи, которые в течение календарного месяца выполнили хотя бы одну операцию идентификации: регистрацию, авторизацию, обновление токена или изменение пароля. Последующие сессии активных пользователей и неактивные пользователи в этом календарном месяце не оплачиваются.

cognito

CODING TIME ‍‍

Чат поддержки AWS Amplify: Telegram

Часть I

В этoй части мы настроим UI компонент аутентификации от AWS Amplify, а в следующей мы создадим его с нуля.

Весь код для этой части можно найти на GitHub.

AWS Amplify

Cognito

Step01

Создаем новый проект ️

react-native init auth

Запускаем проект

iOS

cd auth && react-native run-ios

Android

cd auth && react-native run-android

Step02

Подключаем иконки

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

Добавляем в App.js

import Icon from 'react-native-vector-icons/FontAwesome5'  const App = () => {   return (     <>       <Icon name="comments" size={30} color="#900" />     </>   ) }

Step03

Регистрируем свой AWS account

Регестрируемся согласно этой инструкции и по видеоучебнику чекаем все 5 шагов.

Внимание!!!

Потребуется банковская карта, где должно быть более 1$

Там же смотрим и ставим Amplify Command Line Interface (CLI)

Step04

Инициализация AWS Amplify в проект React Native

В корневой директории проекта React Native инициализируем наш AWS Amplify проект

amplify init

Отвечаем на вопросы:

amplify init

Проект инициализацировался

Step05

Подключаем плагин аутентификации

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

Командой

amplify add auth

подключаем функцию аутентификации. Выбираем конфигурацию по умолчанию. Это добавляет конфигурации ресурсов auth локально в ваш каталог ampify/backend/auth

Выбираем профиль, который мы хотим использовать. default. Enter и как пользователи будут входить в систему. Email(За SMS списывают деньги).

amplify init

Отправляем изменения в облако

amplify push

All resources are updated in the cloud

Step06

Подключаем AWS Amplify в проект React Native ️

Подробности в этой инструкции, а коротко и по прямой так:

yarn add aws-amplify @aws-amplify/core aws-amplify-react-native amazon-cognito-identity-js @react-native-community/netinfo

После установки обязательно заходим в папку ios и ставим поды

cd ios && pod install && cd ..

Step07

Редактируем структуру проекта

Создаем директорию /src и переносим туда файл App.js, переименовывая его в index.js

Правим импорт в /auth/index.js и скрываем будущие предупреждения.

import { AppRegistry, YellowBox } from 'react-native' import App from './src' import { name as appName } from './app.json'  YellowBox.ignoreWarnings([   'Warning: AsyncStorage',     'Warning: componentWillReceiveProps',   'RCTRootView cancelTouches',   'not authenticated',   'Sending `onAnimatedValueUpdate`' ])  //window.LOG_LEVEL = 'DEBUG'  AppRegistry.registerComponent(appName, () => App)

Step08

Минимальная конфигурация проекта и модуль Authenticator

Amplify.configure — конфигурация проекта

Authenticator — Модуль AWS Amplify Authentication предоставляет API-интерфейсы аутентификации и стандартные блоки для разработчиков, которые хотят создавать возможности аутентификации пользователей.

import React from 'react' import {StatusBar} from 'react-native' import Amplify from '@aws-amplify/core' import {Authenticator} from 'aws-amplify-react-native' import awsconfig from '../aws-exports'  Amplify.configure({   ...awsconfig,   Analytics: {     disabled: true,   }, })  const App = () => {   return (     <>       <StatusBar barStyle="dark-content" />       <Authenticator usernameAttributes="email" />     </>   ) }  export default App

Запускаем симулятор, где нас встречает UI компонент аутентификации:

Cognito

Step09

Правим инпуты в App.js

Для этого добавляем signUpConfig

const signUpConfig = {   hideAllDefaults: true,   signUpFields: [     {       label: 'Email',       key: 'email',       required: true,       displayOrder: 1,       type: 'string',     },     {       label: 'Password',       key: 'password',       required: true,       displayOrder: 2,       type: 'password',     },   ], }  <Authenticator    usernameAttributes="email"    signUpConfig={signUpConfig} />

Step10

Меняем тему UI

Создаем точку экспорта наших будущих компонентов /src/components/index.js с содержанием

export * from './AmplifyTheme'

и соответствено создаем сам файл /src/components/AmplifyTheme/index.js темы с содержанием

import { StyleSheet } from 'react-native'  export const deepSquidInk = '#152939' export const linkUnderlayColor = '#FFF' export const errorIconColor = '#30d0fe'  const AmplifyTheme = StyleSheet.create({   container: {     flex: 1,     flexDirection: 'column',     alignItems: 'center',     justifyContent: 'space-around',     paddingTop: 20,     width: '100%',     backgroundColor: '#FFF'   },   section: {     flex: 1,     width: '100%',     padding: 30   },   sectionHeader: {     width: '100%',     marginBottom: 32   },   sectionHeaderText: {     color: deepSquidInk,     fontSize: 20,     fontWeight: '500'   },   sectionFooter: {     width: '100%',     padding: 10,     flexDirection: 'row',     justifyContent: 'space-between',     marginTop: 15,     marginBottom: 20   },   sectionFooterLink: {     fontSize: 14,     color: '#30d0fe',     alignItems: 'baseline',     textAlign: 'center'   },   navBar: {     marginTop: 35,     padding: 15,     flexDirection: 'row',     justifyContent: 'flex-end',     alignItems: 'center'   },   navButton: {     marginLeft: 12,     borderRadius: 4   },   cell: {     flex: 1,     width: '50%'   },   errorRow: {     flexDirection: 'row',     justifyContent: 'center'   },   errorRowText: {     marginLeft: 10   },   photo: {     width: '100%'   },   album: {     width: '100%'   },   button: {     backgroundColor: '#30d0fe',     alignItems: 'center',     padding: 16   },   buttonDisabled: {     backgroundColor: '#85E4FF',     alignItems: 'center',     padding: 16   },   buttonText: {     color: '#fff',     fontSize: 14,     fontWeight: '600'   },   formField: {     marginBottom: 22   },   input: {     padding: 16,     borderWidth: 1,     borderRadius: 3,     borderColor: '#C4C4C4'   },   inputLabel: {     marginBottom: 8   },   phoneContainer: {     display: 'flex',     flexDirection: 'row',     alignItems: 'center'   },   phoneInput: {     flex: 2,     padding: 16,     borderWidth: 1,     borderRadius: 3,     borderColor: '#C4C4C4'   },   picker: {     flex: 1,     height: 44   },   pickerItem: {     height: 44   } })  export { AmplifyTheme }

И подключаем тему в компонент Authenticator src/index.js

import {AmplifyTheme} from './components'  <Authenticator   usernameAttributes="email"   signUpConfig={signUpConfig}   theme={AmplifyTheme} />

AmplifyTheme

Step11

Подключаем локализацию

В нашем случае русский язык

Добавляем экспорт в /src/components/index.js

export * from './Localei18n'

Cоздаем сам файл /src/components/Localei18n/index.js с содержанием

import { NativeModules, Platform } from 'react-native' import { I18n } from '@aws-amplify/core'  let langRegionLocale = 'en_US'  // If we have an Android phone if (Platform.OS === 'android') {   langRegionLocale = NativeModules.I18nManager.localeIdentifier || '' } else if (Platform.OS === 'ios') {   langRegionLocale = NativeModules.SettingsManager.settings.AppleLocale || '' }  const authScreenLabels = {   en: {     'Sign Up': 'Create new account',     'Sign Up Account': 'Create a new account'   },   ru: {     'Sign Up': 'Создать аккаунт',     'Forgot Password': 'Забыли пароль?',     'Sign In Account': 'Войдите в систему',     'Enter your email': 'Введите email',     'Enter your password': 'Введите пароль',     Password: 'Пароль',     'Sign In': 'Вход',     'Please Sign In / Sign Up': 'Войти / Создать аккаунт',     'Sign in to your account': 'Войдите в свой аккаунт',     'Create a new account': 'Cоздайте свой аккаунт',     'Confirm a Code': 'Подтвердите код',     'Confirm Sign Up': 'Подтвердите регистрацию',     'Resend code': 'Еще отправить код',     'Back to Sign In': 'Вернуться к входу',     Confirm: 'Подтвердить',     'Confirmation Code': 'Код подтверждения',     'Sign Out': 'Выход'   } }  // "en_US" -> "en", "es_CL" -> "es", etc const languageLocale = langRegionLocale.substring(0, 2) I18n.setLanguage(languageLocale) I18n.putVocabularies(authScreenLabels)  const Localei18n = () => null  export { Localei18n }

И подключаем компонент Localei18n в src/index.js

import {    AmplifyTheme,    Localei18n } from './components'  <Localei18n /> <Authenticator   usernameAttributes="email"   signUpConfig={signUpConfig}   theme={AmplifyTheme} />

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

Localei18n

Done

Часть II

Во-первых стандартный UI от Amplify далеко не всегда удовлетворяет UX приходящий со стороны заказчика

Во-вторых в официальной документации Amplify написано:

Data is stored unencrypted when using standard storage adapters (localStorage in the browser and AsyncStorage on React Native). Amplify gives you the option to use your own storage object to persist data. With this, you could write a thin wrapper around libraries like:
react-native-keychain
react-native-secure-storage
Expo’s secure store

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

Весь код для этой части можно найти на GitHub.

AWS Amplify

Step01

UI Kit

Мы будем использовать наш UI Kit, но вы можете легко заменить его своим или любым другим.

Подключаем библиотеку компонентов согласно этой статьи.

Step02

Ставим навигацию react-navigation 5, также как написано здесь (на момент написание этой статьи):

yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view @react-navigation/stack

Добавляем поды под iOS

cd ios && pod install && cd ..

Рекомендую после каждой установки запускать приложение под iOS и Android, чтобы потом не искать библиотеку из-за которой приложение падает.

Step03

react-native-keychain

Ставим библиотеку react-native-keychain — это безопасное хранилище ключей react-native-keychain для React Native.

yarn add react-native-keychain

Добавляем поды под iOS

cd ios && pod install && cd ..

Согласно тому, что нам говорит официальная документация:

При использовании аутентификации с AWS Amplify вам не нужно обновлять токены Amazon Cognito вручную. Токены автоматически обновляются библиотекой при необходимости. Токены безопасности, такие как IdToken или AccessToken, хранятся в localStorage для браузера и в AsyncStorage для React Native. Если вы хотите хранить эти токены в более безопасном месте или используете Amplify на стороне сервера, вы можете предоставить свой собственный объект хранения для хранения этих токенов.

конфигурируем наш src/index.js

import React from 'react' import Amplify from '@aws-amplify/core' import * as Keychain from 'react-native-keychain' import { ThemeProvider, DarkTheme, LightTheme } from 'react-native-unicorn-uikit' import { useColorScheme } from 'react-native-appearance' import AppNavigator from './AppNavigator' import awsconfig from '../aws-exports'  const MEMORY_KEY_PREFIX = '@MyStorage:' let dataMemory = {}  class MyStorage {   static syncPromise = null    static setItem(key, value) {     Keychain.setGenericPassword(MEMORY_KEY_PREFIX + key, value)     dataMemory[key] = value     return dataMemory[key]   }    static getItem(key) {     return Object.prototype.hasOwnProperty.call(dataMemory, key) ? dataMemory[key] : undefined   }    static removeItem(key) {     Keychain.resetGenericPassword()     return delete dataMemory[key]   }    static clear() {     dataMemory = {}     return dataMemory   } }  Amplify.configure({   ...awsconfig,   Analytics: {     disabled: false   },   storage: MyStorage })  const App = () => {   const scheme = useColorScheme()   return (     <>       <ThemeProvider theme={scheme === 'dark' ? DarkTheme : LightTheme}>         <AppNavigator />       </ThemeProvider>     </>   ) }  export default App

Step04

Константы

Чтобы не копипастить одни и те же значения, мы создаем файл с константами для общего использования в компонентах src/constants.js

import { Dimensions } from 'react-native'  export const BG = '#0B0B0B' export const PINK = '#F20AF5' export const PURPLE = '#7A1374' export const BLUE = '#00FFFF' export const GREEN = '#2E7767' export const RED = '#FC2847' export const LABEL_COLOR = BLUE export const INPUT_COLOR = PINK export const ERROR_COLOR = RED export const HELP_COLOR = '#999999' export const BORDER_COLOR = BLUE export const DISABLED_COLOR = '#777777' export const DISABLED_BACKGROUND_COLOR = '#eeeeee'  export const win = Dimensions.get('window') export const W = win.width export const H = win.height  export const Device = {   // eslint-disable-next-line   select(variants) {     if (W >= 300 && W <= 314) return variants.mobile300 || {}     if (W >= 315 && W <= 341) return variants.iphone5 || {}     if (W >= 342 && W <= 359) return variants.mobile342 || {}     if (W >= 360 && W <= 374) return variants.mi5 || {}     if (W >= 375 && W <= 399) return variants.iphone678 || {}     if (W >= 400 && W <= 409) return variants.mobile400 || {}     if (W >= 410 && W <= 414) return variants.googlePixel || {}     if (W >= 415 && W <= 434) return variants.mobile415 || {}     if (W >= 435 && W <= 480) return variants.redmiNote5 || {}   } }  export const goBack = navigation => () => navigation.goBack()  export const onScreen = (screen, navigation, obj) => () => {   navigation.navigate(screen, obj) }  export const goHome = navigation => () => navigation.popToTop()()

Step05

AppNavigator

Создаем файл с конфигурацией навигации для нашей кастомной аутентификации src/AppNavigator.js

import * as React from 'react' import { createStackNavigator } from '@react-navigation/stack' import { Hello } from './screens/Authenticator'  const Stack = createStackNavigator()  const AppNavigator = () => {      return (     <Stack.Navigator       screenOptions={{         headerShown: false       }}       initialRouteName="HELLO"     >       <Stack.Screen name="HELLO" component={Hello} />     </Stack.Navigator>   ) }  export default AppNavigator

Step06

Hello screen

Создаем точку входа для нашых экранов аутентификации src/screens/Authenticator/index.js

Hello screen

Где для начала мы подключаем экран приветствия

export * from './Hello'

После создаем его src/screens/Authenticator/Hello/index.js

В хуке useEffect мы выполняем проверку на наличие токена пользователя, где в случае true мы отправляемся на экран User, а в случае false остаемся на этом экране.

import React, { useEffect, useState } from 'react' import { Auth } from 'aws-amplify' import * as Keychain from 'react-native-keychain' import { AppContainer, Button, Space, H6 } from 'react-native-unicorn-uikit' import { onScreen } from '../../../constants'  const Hello = ({ navigation }) => {   const [loading, setLoading] = useState(false)   useEffect(() => {     setLoading(true)     const key = async () => {       try {         const credentials = await Keychain.getInternetCredentials('auth')          if (credentials) {           const { username, password } = credentials           const user = await Auth.signIn(username, password)           setLoading(false)           user && onScreen('USER', navigation)()         } else {           setLoading(false)         }       } catch (err) {         console.log('error', err) // eslint-disable-line         setLoading(false)       }     }     key()   }, []) // eslint-disable-line   return (     <AppContainer loading={loading}>       <Space height={200} />       <Button title="Sign In" onPress={onScreen('SIGN_IN', navigation)} />       <Space height={10} />       <H6 title="or" textStyle={{ alignSelf: 'center' }} />       <Space height={15} />       <Button title="Sign Up" onPress={onScreen('SIGN_UP', navigation)} />     </AppContainer>   ) }  export { Hello }

Собираем приложение и встречаем экран приветствия.

SignUp screen

Создаем экран регистрации SIGN_UP src/screens/Authenticator/SignUp/index.js, где для аутентификации мы используем метод Auth.signUp

SignUp

import React, { useState } from 'react' import { Auth } from 'aws-amplify' import * as Keychain from 'react-native-keychain' import { Formik } from 'formik' import * as Yup from 'yup' import { AppContainer, Space, Button, Input, TextError } from 'react-native-unicorn-uikit' import { onScreen, goBack } from '../../../constants'  const SignUp = ({ navigation }) => {   const [loading, setLoading] = useState(false)   const [error, setError] = useState('')    const _onPress = async (values) => {     const { email, password, passwordConfirmation } = values     if (password !== passwordConfirmation) {       setError('Passwords do not match!')     } else {       setLoading(true)       setError('')       try {         const user = await Auth.signUp(email, password)         await Keychain.setInternetCredentials('auth', email, password)         user && onScreen('CONFIRM_SIGN_UP', navigation, { email, password })()         setLoading(false)       } catch (err) {         setLoading(false)         if (err.code === 'UserNotConfirmedException') {           setError('Account not verified yet')         } else if (err.code === 'PasswordResetRequiredException') {           setError('Existing user found. Please reset your password')         } else if (err.code === 'NotAuthorizedException') {           setError('Forgot Password?')         } else if (err.code === 'UserNotFoundException') {           setError('User does not exist!')         } else {           setError(err.code)         }       }     }   }    return (     <>       <AppContainer onPress={goBack(navigation)} title="Sign Up" loading={loading}>         <Space height={80} />         <Formik           initialValues={{ email: '', password: '', passwordConfirmation: '' }}           onSubmit={(values) => _onPress(values)}           validationSchema={Yup.object().shape({             email: Yup.string().email().required(),             password: Yup.string().min(6).required(),             passwordConfirmation: Yup.string().min(6).required()           })}         >           {({ values, handleChange, errors, setFieldTouched, touched, isValid, handleSubmit }) => (             <>               <Input                 name="email"                 value={values.email}                 onChangeText={handleChange('email')}                 onBlur={() => setFieldTouched('email')}                 placeholder="E-mail"                 touched={touched}                 errors={errors}                 autoCapitalize="none"               />               <Input                 name="password"                 value={values.password}                 onChangeText={handleChange('password')}                 onBlur={() => setFieldTouched('password')}                 placeholder="Password"                 touched={touched}                 errors={errors}                 secureTextEntry               />               <Input                 name="passwordConfirmation"                 value={values.passwordConfirmation}                 onChangeText={handleChange('passwordConfirmation')}                 onBlur={() => setFieldTouched('passwordConfirmation')}                 placeholder="Password confirm"                 touched={touched}                 errors={errors}                 secureTextEntry               />               <Space height={30} />               {error !== '' && <TextError title={error} textStyle={{ alignSelf: 'center' }} />}               <Button title="Sign Up" disabled={!isValid} onPress={handleSubmit} formik />             </>           )}         </Formik>       </AppContainer>     </>   ) }  export { SignUp }

Step08

ConfirmSignUp screen

После успешного ответа с сервера, мы переходим на экран подтверждения и ввода кода, пришедшего нам на почту. Для этого создаем экран CONFIRM_SIGN_UP src/screens/Authenticator/ConfirmSignUp/index.js

ConfirmSignUp

import React, { useState } from 'react' import { Auth } from 'aws-amplify' import { Formik } from 'formik' import * as Yup from 'yup' import { AppContainer, Button, Space, ButtonLink, TextError, Input } from 'react-native-unicorn-uikit' import { onScreen, goBack } from '../../../constants'  const ConfirmSignUp = ({ route, navigation }) => {   const [loading, setLoading] = useState(false)   const [error, setError] = useState('')    const _onPress = async (values) => {     setLoading(true)     setError('')     try {       const { code } = values       const { email, password } = route.params       await Auth.confirmSignUp(email, code, { forceAliasCreation: true })       const user = await Auth.signIn(email, password)       user && onScreen('USER', navigation)()       setLoading(false)     } catch (err) {       setLoading(false)       setError(err.message)       if (err.code === 'UserNotConfirmedException') {         setError('Account not verified yet')       } else if (err.code === 'PasswordResetRequiredException') {         setError('Existing user found. Please reset your password')       } else if (err.code === 'NotAuthorizedException') {         setError('Forgot Password?')       } else if (err.code === 'UserNotFoundException') {         setError('User does not exist!')       }     }   }    const _onResend = async () => {     try {       const { email } = route.params       await Auth.resendSignUp(email)     } catch (err) {       setError(err.message)     }   }    return (     <>       <AppContainer title="Confirmation" onPress={goBack(navigation)} loading={loading}>         <Formik           initialValues={{ code: '' }}           onSubmit={(values) => _onPress(values)}           validationSchema={Yup.object().shape({             code: Yup.string().min(6).required()           })}         >           {({ values, handleChange, errors, setFieldTouched, touched, isValid, handleSubmit }) => (             <>               <Space height={180} />               <Input                 name="code"                 value={values.code}                 onChangeText={handleChange('code')}                 onBlur={() => setFieldTouched('code')}                 placeholder="Insert code"                 touched={touched}                 errors={errors}               />               <ButtonLink title="Resend code?" onPress={_onResend} textStyle={{ alignSelf: 'center' }} />               {error !== 'Forgot Password?' && <TextError title={error} />}               <Button title="Confirm" disabled={!isValid} onPress={handleSubmit} formik />               <Space height={50} />             </>           )}         </Formik>       </AppContainer>     </>   ) }  export { ConfirmSignUp }

ResendSignUp

Если код не пришел, то мы должны предоставить пользователю возможность отправить код повторно. Для этого на кнопку Resend code? мы вешаем метод Auth.resendSignUp(userInfo.email)
В случае успешного вызова метода

Auth.confirmSignUp(email, code, { forceAliasCreation: true })

мы должны вызывать метод

Auth.signIn(email, password)

Step09

User screen

В случае успеха переходим на экран USER, который мы создаем c кнопкой выхода из приложения и очисткой токенов src/screens/Authenticator/User/index.js

User screen

import React, { useState, useEffect } from 'react' import { Auth } from 'aws-amplify' import * as Keychain from 'react-native-keychain' import { AppContainer, Button } from 'react-native-unicorn-uikit' import { goHome } from '../../../constants'  const User = ({ navigation }) => {   const [loading, setLoading] = useState(false)   const [error, setError] = useState('')    useEffect(() => {     const checkUser = async () => {       await Auth.currentAuthenticatedUser()     }     checkUser()   })    const _onPress = async () => {     setLoading(true)     try {       await Auth.signOut()       await Keychain.resetInternetCredentials('auth')       goHome(navigation)()     } catch (err) {       setError(err.message)     }   }    return (     <AppContainer message={error} loading={loading}>       <Button title="Sign Out" onPress={_onPress} />     </AppContainer>   ) }  export { User }

Step10

SignIn screen

После того, как зарегистрировали пользователя, мы должны предоставить юзеру возможность войти в приложение через логин и пароль. Для этого мы создаем экран SIGN_IN src/screens/Authenticator/SignIn/index.js

SignIn screen

import React, { useState } from 'react' import { Auth } from 'aws-amplify' import * as Keychain from 'react-native-keychain' import { Formik } from 'formik' import * as Yup from 'yup' import { AppContainer, Button, Space, ButtonLink, TextError, Input } from 'react-native-unicorn-uikit' import { onScreen, goBack } from '../../../constants'  const SignIn = ({ navigation }) => {   const [userInfo, setUserInfo] = useState('')   const [loading, setLoading] = useState(false)   const [error, setError] = useState('')    const _onPress = async (values) => {     setLoading(true)     setError('')     try {       const { email, password } = values       const user = await Auth.signIn(email, password)       await Keychain.setInternetCredentials('auth', email, password)       user && onScreen('USER', navigation)()       setLoading(false)     } catch (err) {       setLoading(false)       if (err.code === 'UserNotConfirmedException') {         setError('Account not verified yet')       } else if (err.code === 'PasswordResetRequiredException') {         setError('Existing user found. Please reset your password')       } else if (err.code === 'NotAuthorizedException') {         setError('Forgot Password?')       } else if (err.code === 'UserNotFoundException') {         setError('User does not exist!')       } else {         setError(err.code)       }     }   }    return (     <>       <AppContainer onPress={goBack(navigation)} title="Sign In" loading={loading}>         <Space height={140} />         <Formik           initialValues={{ email: '', password: '' }}           onSubmit={(values) => _onPress(values) && setUserInfo(values.email)}           validationSchema={Yup.object().shape({             email: Yup.string().email().required(),             password: Yup.string().min(6).required()           })}         >           {({ values, handleChange, errors, setFieldTouched, touched, isValid, handleSubmit }) => (             <>               <Input                 name="email"                 value={values.email}                 onChangeText={handleChange('email')}                 onBlur={() => setFieldTouched('email')}                 placeholder="E-mail"                 touched={touched}                 errors={errors}                 autoCapitalize="none"               />               <Input                 name="password"                 value={values.password}                 onChangeText={handleChange('password')}                 onBlur={() => setFieldTouched('password')}                 placeholder="Password"                 touched={touched}                 errors={errors}                 secureTextEntry               />               {error !== 'Forgot Password?' && <TextError title={error} textStyle={{ alignSelf: 'center' }} />}               {error === 'Forgot Password?' && (                 <ButtonLink                   title={error}                   onPress={onScreen('FORGOT', navigation, userInfo)}                   textStyle={{ alignSelf: 'center' }}                 />               )}               <Space height={30} />               <Button title="Sign In" disabled={!isValid} onPress={handleSubmit} formik />             </>           )}         </Formik>       </AppContainer>     </>   ) }  export { SignIn }

Step11

Forgot password screen

В случае успеха, мы отправляем юзера на экран USER, который мы уже ранее сделали, а если юзер забыл или не правильно ввел пароль, то мы показываем ошибку Forgot Password? и предлагаем сбросить пароль.

Forgot password

Для этого мы создаем экран FORGOT src/screens/Authenticator/Forgot/index.js

Forgot password

import React, { useState } from 'react' import { Auth } from 'aws-amplify' import { Formik } from 'formik' import * as Yup from 'yup' import { AppContainer, Button, Input } from 'react-native-unicorn-uikit' import { onScreen, goBack } from '../../../constants'  const Forgot = ({ route, navigation }) => {   const [loading, setLoading] = useState(false)   const [error, setError] = useState('')    const _onPress = async (values) => {     setLoading(true)     try {       const { email } = values       const user = await Auth.forgotPassword(email)       user && onScreen('FORGOT_PASSWORD_SUBMIT', navigation, email)()       setLoading(false)     } catch (err) {       setError(error)     }   }    return (     <>       <AppContainer title="Forgot" onPress={goBack(navigation)} loading={loading}>         <Formik           initialValues={{ email: route.params }}           onSubmit={(values) => _onPress(values)}           validationSchema={Yup.object().shape({             email: Yup.string().email().required()           })}         >           {({ values, handleChange, errors, setFieldTouched, touched, isValid, handleSubmit }) => (             <>               <Input                 name="email"                 value={values.email}                 onChangeText={handleChange('email')}                 onBlur={() => setFieldTouched('email')}                 placeholder="E-mail"                 touched={touched}                 errors={errors}                 autoCapitalize="none"               />               <Button title="Confirm" disabled={!isValid} onPress={handleSubmit} formik />             </>           )}         </Formik>       </AppContainer>     </>   ) }  export { Forgot }

Step12

Forgot Password Submit

После подтверждения e-mail, мы вызываем метод Auth.forgotPassword(email) и в случае, если такой юзер есть, то отправляем пользователя на экран FORGOT_PASSWORD_SUBMIT src/screens/Authenticator/ForgotPassSubmit/index.js

ForgotPassSubmit

import React, { useState } from 'react' import { Platform } from 'react-native' import { Auth } from 'aws-amplify' import * as Keychain from 'react-native-keychain' import { Formik } from 'formik' import * as Yup from 'yup' import { AppContainer, Button, Space, Input, TextError } from 'react-native-unicorn-uikit' import { onScreen, goBack } from '../../../constants'  const ForgotPassSubmit = ({ route, navigation }) => {   const [loading, setLoading] = useState(false)   const [error, setError] = useState('')    const _onPress = async (values) => {     setLoading(true)     try {       const { email, code, password } = values       await Auth.forgotPasswordSubmit(email, code, password)       await Keychain.setInternetCredentials('auth', email, password)       onScreen('USER', navigation)()       setLoading(false)     } catch (err) {       setLoading(false)       setError(err.message)     }   }    return (     <>       <AppContainer title="Confirmation" onPress={goBack(navigation)} loading={loading}>         <Space height={Platform.OS === 'ios' ? 20 : 150} />         <Formik           initialValues={{ email: route.params, code: '', password: '', passwordConfirmation: '' }}           onSubmit={(values) => _onPress(values)}           validationSchema={Yup.object().shape({             email: Yup.string().email().required(),             code: Yup.string().min(6).required(),             password: Yup.string().min(6).required(),             passwordConfirmation: Yup.string().min(6).required()           })}         >           {({ values, handleChange, errors, setFieldTouched, touched, isValid, handleSubmit }) => (             <>               <Input                 name="email"                 value={values.email}                 onChangeText={handleChange('email')}                 onBlur={() => setFieldTouched('email')}                 placeholder="E-mail"                 touched={touched}                 errors={errors}                 autoCapitalize="none"               />               <Input                 name="code"                 value={values.code}                 onChangeText={handleChange('code')}                 onBlur={() => setFieldTouched('code')}                 placeholder="Code"                 touched={touched}                 errors={errors}               />               <Input                 name="password"                 value={values.password}                 onChangeText={handleChange('password')}                 onBlur={() => setFieldTouched('password')}                 placeholder="Password"                 touched={touched}                 errors={errors}                 secureTextEntry               />               <Input                 name="passwordConfirmation"                 value={values.passwordConfirmation}                 onChangeText={handleChange('passwordConfirmation')}                 onBlur={() => setFieldTouched('passwordConfirmation')}                 placeholder="Password confirm"                 touched={touched}                 errors={errors}                 secureTextEntry               />               {error !== '' && <TextError title={error} textStyle={{ alignSelf: 'center' }} />}               <Space height={30} />               <Button title="Confirm" disabled={!isValid} onPress={handleSubmit} formik />             </>           )}         </Formik>       </AppContainer>     </>   ) }  export { ForgotPassSubmit }

где после ввода кода, отправленного на почту, нового пароля и его подтверждения, мы вызываем метод смены пароля

Auth.forgotPasswordSubmit(email, code, password)

успех которого отправляет юзера на экран USER.

Step13

Связывание экранов

Подключаем все созданые компоненты в src/screens/Authenticator/index.js

export * from './Hello' export * from './User' export * from './SignIn' export * from './SignUp' export * from './Forgot' export * from './ForgotPassSubmit' export * from './ConfirmSignUp'

Step14

Udpate AppNavigator

Обновляем файл конфигурации навигации:

import * as React from 'react' import { createStackNavigator } from '@react-navigation/stack' import { Hello, SignUp, SignIn, ConfirmSignUp, User, Forgot, ForgotPassSubmit } from './screens/Authenticator'  const Stack = createStackNavigator()  const AppNavigator = () => {   return (     <Stack.Navigator       screenOptions={{         headerShown: false       }}       initialRouteName="HELLO"     >       <Stack.Screen name="HELLO" component={Hello} />       <Stack.Screen name="SIGN_UP" component={SignUp} />       <Stack.Screen name="SIGN_IN" component={SignIn} />       <Stack.Screen name="FORGOT" component={Forgot} />       <Stack.Screen name="FORGOT_PASSWORD_SUBMIT" component={ForgotPassSubmit} />       <Stack.Screen name="CONFIRM_SIGN_UP" component={ConfirmSignUp} />       <Stack.Screen name="USER" component={User} />     </Stack.Navigator>   ) }  export default AppNavigator

Step15

Clean Up

Так как мы используем кастомную тему, то удаляем компоненты AmplifyTheme и Localei18n

Step16

Debug

Для того, чтобы понимать, что происходит с токенами в вашем приложении, добавьте в корневой /index.js

window.LOG_LEVEL = 'DEBUG'

Запускаем приложение и получаем кастомную аутентификацию.

Done

В продолжение темы смотреть статью DataStore — CRUD (Create Read Update Delete)

Become a Patron!

ссылка на оригинал статьи https://habr.com/ru/post/504344/