company_banner

OAuth-авторизация в Mozilla Thunderbird: от зарождения до релиза

  • Tutorial



Некоторое время назад мы рассказывали о том, как в Mail.Ru реализован сбор почты с использованием протокола OAuth 2.0. Мы продолжаем повышать безопасность почты и продвигать стандарт OAuth 2.0 в массы. И сегодня расскажем о том, как мы добавили OAuth-авторизацию в почтовый клиент Mozilla Thunderbird. На этом примере мы разберем процесс внесения новой фичи в продукт с открытым исходным кодом, от создания тикета до релиза. Если вы давно хотели сделать свой первый pull request, но не знали как, — читайте нашу историю.



1. Общая схема работы


На действия пользователя Thunderbird открывает веб-вью с адресом для OAuth-авторизации. Если пользователь успешно прошел процедуру авторизации и согласился предоставить приложению доступ к своим данным, то мы перенаправляем пользователя на адрес, указанный в параметре redirect_uri. Так приложение получит авторизационный токен и сможет использовать его для работы с нашей почтовой службой. Адрес для запроса токена:


https://o2.mail.ru/login
?response_type=code
&client_id=<идентификатор приложения>
&redirect_uri=http%3A%2F%2Flocalhost

Стоит заметить, что значение localhost для параметра redirect_uri приложение задает самостоятельно, а параметр state (используется клиентом для поддержки связи между запросом и колбэком) не передает вовсе. Ниже представлена схема взаимодействия приложения с сервисом:




2. Как устроен процесс интеграции, основные этапы


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


Поскольку Thunderbird — это продукт компании Mozilla, мы сразу отправились на MDN. Так мы быстро получили общее представление об основных этапах интеграции:


  1. Тикет на добавление почтового клиента.
  2. Тикет на добавление конфигурации в ISP-базу.
  3. Патч в репозиторий comm-central.
  4. Патч в ISP-базу.
  5. Тестовая сборка.
  6. Сохранение обратной совместимости.
  7. Тестирование функциональности в ранних релизах.
  8. Тестирование релиза.

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


2.1. Тикет на добавление почтового клиента


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


  • Продукт: Структура репозитория comm-central разделена на независимые продукты. С этим у нас проблем не возникло, поскольку название продукта MailNews сразу упоминается на стартовой странице.


  • Компонент: Напротив этого пункта есть подсказка, однако здесь и так интуитивно понятно, что работа связана с сетью, поэтому выбираем Networking.


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


  • Платформа: В нашем случае продукт платформонезависимый (all).


  • Важность: Поскольку мы расширяем функциональность, тип будет enhancement.


  • Ключевые слова: Список ключевых слов ограничен. Беглый поиск по похожим тикетам подсказал выбрать feature, user-doc-needed.

Совет: чтобы случайно не пропустить какой-либо этап, добавьте к себе календарь.

2.2. Тикет на добавление конфигурации в ISP-базу


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


2.3. Патч в репозиторий comm-central


Если вы обратили внимание, сообщество Mozilla очень трепетно относится к документированию своих продуктов. Руководство по сборке продукта, стилистике написания программного кода и пр. Все ссылки на это располагаются в одном месте и не требуют, как это часто бывает с другими продуктами, прохождения некоего квеста. Сразу скажу, что никакого "rocket science" в добавлении нового OAuth-провайдера в Thunderbird нет, — это становится понятно после грепа по репозиторию и беглого ознакомления с исходным кодом. Несмотря на то что файлов с ключевым словом OAuth было довольно много:


➜ hg clone http://hg.mozilla.org/comm-central
...
➜ hg grep --ignore-case --files-with-matches oauth | wc -l
      91

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


mailnews/base/util/OAuth2Providers.jsm 

Не буду томить, просто смотрите дифф:


➜ hg log -p -l 1

changeset: [draft] 18512:a2f404184ac0 support_oauth_mail_ru_1231642 tip
author:    Alexander Abashkin <monolithed@gmail.com>
date:      Mon, 14 Dec 2015 15:17:09 +0300 (4 months ago)
summary:   Bug 1231642 - Log in to Mail.Ru (IMAP/SMTP) using OAuth

M mailnews/base/util/OAuth2Providers.jsm

diff --git a/mailnews/base/util/OAuth2Providers.jsm b/mailnews/base/util/OAuth2Providers.jsm
--- a/mailnews/base/util/OAuth2Providers.jsm
+++ b/mailnews/base/util/OAuth2Providers.jsm
@@ -10,32 +10,41 @@ var EXPORTED_SYMBOLS = ["OAuth2Providers
 var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;

 // map of hostnames to [issuer, scope]
 var kHostnames = new Map([
   ["imap.googlemail.com", ["accounts.google.com", "https://mail.google.com/"]],
   ["smtp.googlemail.com", ["accounts.google.com", "https://mail.google.com/"]],
   ["imap.gmail.com", ["accounts.google.com", "https://mail.google.com/"]],
   ["smtp.gmail.com", ["accounts.google.com", "https://mail.google.com/"]],
+  ["imap.mail.ru", ["o2.mail.ru", "mail.imap"]],
+  ["smtp.mail.ru", ["o2.mail.ru", "mail.imap"]],
 ]);

 // map of issuers to appKey, appSecret, authURI, tokenURI 
 var kIssuers = new Map ([
   ["accounts.google.com", [
     '406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com',
     'xxxxxxxxxxxx',
     'https://accounts.google.com/o/oauth2/auth',
     'https://www.googleapis.com/oauth2/v3/token'
   ]],
+  ["o2.mail.ru", [
+    'thunderbird',
+    'xxxxxxxxxxxx',
+    'https://o2.mail.ru/login',
+    'https://o2.mail.ru/token'
+  ]],
 ]);

Если вы обратили внимание, то, к сожалению, мы пока не поддержали новый протокол, который позволяет динамически регистрировать клиент, но мы над этим работаем! И далее аттач патча по номеру тикета:


hg diff -g > 1231642.patch

P. S. Будьте готовы к тому, что клонирование репозитория требует до 5 Гб свободного места на диске.


2.4. Патч в ISP-базу


Эта конфигурация необходима для выбора протокола, который будет использоваться по умолчанию. Пример файла-автоконфига: https://autoconfig.thunderbird.net/v1.1/mail.ru. SVN-репозиторий ISP находится по следующему адресу:


https://svn.mozilla.org/mozillamessaging.com/sites/autoconfig.mozillamessaging.com/

Поскольку мы уже имели дело с этим конфигом ранее, то показать дифф будет проще, чем рассказать:


➜ svn diff trunk/mail.ru | vi -R -

--- trunk/mail.ru|  (revision 150325)
+++ trunk/mail.ru|  (working copy)
@@ -13,6 +13,7 @@
        <incomingServer type="imap">
            <hostname>imap.mail.ru</hostname>
            <port>993</port>
            <socketType>SSL</socketType>
            <username>%EMAILADDRESS%</username>
+           <authentication>OAuth2</authentication>
            <authentication>password-cleartext</authentication>
        </incomingServer>

        <incomingServer type="imap">
            <hostname>imap.mail.ru</hostname>
            <port>143</port>
            <socketType>STARTTLS</socketType>
            <username>%EMAILADDRESS%</username>
+           <authentication>OAuth2</authentication>
            <authentication>password-cleartext</authentication>
        </incomingServer>

        <outgoingServer type="smtp">
            <hostname>smtp.mail.ru</hostname>
            <port>465</port>
            <socketType>SSL</socketType>
            <username>%EMAILADDRESS%</username>
+           <authentication>OAuth2</authentication>
            <authentication>password-cleartext</authentication>
        </outgoingServer>

        <outgoingServer type="smtp">
            <hostname>smtp.mail.ru</hostname>
            <port>587</port>
            <socketType>STARTTLS</socketType>
            <username>%EMAILADDRESS%</username>
+           <authentication>OAuth2</authentication>
            <authentication>password-cleartext</authentication>
        </outgoingServer>

На этом этапе торопиться не стоит, даже если ваш сервер уже поддерживает OAuth-авторизацию, ведь можно получить сайд-эффект в виде неработающей авторизации. В качестве альтернативы вы можете разместить файл автоконфига на своем сервере: https://autoconfig.mail.ru/mail/config-v1.1.xml. В таком случае у вашего файла будет более высокий приоритет и вы сможете самостоятельно управлять способом авторизации не только на этапе тестирования. Если у вашего почтового сервиса есть алиасы доменов, то переживать не стоит: ISP-сервер смотрит на MX-записи. Более подробно об этом способе конфигурации сервера смотрите здесь.


2.5. Тестовая сборка


Клонируем репозиторий:


hg clone http://hg.mozilla.org/comm-central
cd comm-central
python client.py checkout

Добавляем конфигурацию для тестового окружения:


➜ echo $'ac_add_options --enable-application=mail\nac_add_options --enable-debug' > .mozconfig

Собираем проект:


./mozilla/mach build

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


./mozilla/mach mercurial-setup --update-only

Более подробно о сборке проекта смотрите здесь.


Возможные проблемы:


Для OS X 10.9–10.10 (в 10.11 эта опция мешает сборке) может потребоваться добавить следующую опцию:


echo 'ac_add_options --with-macos-sdk=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/'

Также в процессе сборки может возникнуть требование установить autoconf 2.13:


fx-team  ./mach build
/usr/bin/make -f client.mk -s
Adding client.mk options from :
    MOZ_OBJDIR=/Applications/MAMP/htdocs/fx-team/obj-x86_64-apple-darwin14.0.0
    OBJDIR=/Applications/MAMP/htdocs/fx-team/obj-x86_64-apple-darwin14.0.0
/Applications/MAMP/htdocs/fx-team/client.mk:304: *** Could not find autoconf 2.13.  Stop.
make: *** [build] Error 2
0 compiler warnings present.

Решение для OS X:


brew rm autoconf
brew tap homebrew/versions
brew install autoconf213

Возможно, это наша вина, но по каким-то причинам файл configure сгенерировался с синтаксическими ошибками:


0:00.70 *** Fix above errors and then restart with\
0:00.70                "/opt/local/bin/gmake -f client.mk build"
0:00.71 /comm-central/client.mk:361: ошибка выполнения рецепта для цели «configure»
0:00.71 gmake[1]: *** [configure] Ошибка 1

Решение:


rm configure && ./mozilla/mach build

Такая ошибка свидетельствует об отсутствии в системе компилятора YASM.


0:09.52 configure:21252: checking for CoreMedia/CoreMedia.h
0:09.52 configure:21262: /usr/bin/clang -E -Qunused-arguments  conftest.c >/dev/null 2>conftest.out
0:09.52 configure: error: yasm is a required build tool for this architecture when webm is enabled. You may either install yasm or --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.
0:09.52 *** Fix above errors and then restart with\
0:09.52                "/opt/local/bin/gmake -f client.mk build"
0:09.52 /comm-central/client.mk:361: ошибка выполнения рецепта для цели «configure»
0:09.52 gmake[1]: *** [configure] Ошибка 1

Решение (для OS X):


brew install yasm && ./mozilla/mach build

Информацию о возможных проблемах сборки можно посмотреть в файле client.mk. Будьте готовы к тому, что исходный код проекта и сборка будет занимать на диске 8,3 Гб!


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


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


./mozilla/mach run

Команда run сама найдет путь к приложению и запустит его. В нашем случае после сборки приложение расположилось по следующему пути:


./obj-x86_64-apple-darwin15.0.0/dist/DailyDebug.app/Contents/MacOS/thunderbird

Автоматизированное тестирование:


Для автоматизации тестирования Thunderbird использует фреймворк MozMill и XPCShell. Запускаем модульные тесты:


./mozilla/mach xpcshell-test

Более подробную информацию о модульном тестировании смотрите ниже по ссылкам:


  1. Руководство по XPCShell.
  2. Инструкция по запуску XPCShell-тестов в продукте MailNews.
  3. Информация о фреймворке для тестирования AsyncTestUtils.

Запускаем интеграционные тесты:


./build/pymake/make.py mozmill

Для интеграционного тестирования используется фреймворк MozMill.


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


Руководство по Treeherder CI смотрите здесь.


2.6. Тестирование функциональности в ранних релизах


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


Общая схема этапов релиза выглядит так:



Релиз-цикл каждого этапа занимает шесть недель.


2.7. Сохранение обратной совместимости


Для клиентов, которые еще не обновились до 45-го релиза, должна работать стандартная схема авторизации. И если об этом не подумать заранее, то пользователь всегда будет видеть ошибку авторизации (если вручную не сменит способ авторизации):





Для того чтобы сохранить обратную совместимость, мы стали отдавать конфигурационный файл, ориентируясь на User-Agent:


location /mail/config-v1.1.xml {
    if ($http_user_agent ~ Thunderbird/(\d|[1-3]\d|4[0-4])\.) {
        rewrite config-v1\.1\.xml /mail/original.config-v1.1.xml;
    }
}

Выставляем заголовок Vary: User-Agent


add_header Vary User-Agent;

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


➜ curl 'https://autoconfig.mail.ru/mail/config-v1.1.xml' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0 Lightning/4.0.5.2' 2>/dev/null | fgrep -i oauth

➜ curl 'https://autoconfig.mail.ru/mail/config-v1.1.xml' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/45.0.0 Lightning/4.0.5.2' 2>/dev/null | fgrep -i oauth
            <authentication>OAuth2</authentication>
            <authentication>OAuth2</authentication>
            <authentication>OAuth2</authentication>
            <authentication>OAuth2</authentication>

2.8. Тестирование релиза


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


3. Сценарий использования


Способов добавления почтового аккаунта в Thunderbird несколько, однако все они сводятся к одним и тем же действиям, поэтому рассмотрим самый очевидный:


1. Открываем стартовую страницу. В разделе создания нового почтового аккаунта выбираем Email:




2. Пропускаем этот шаг, поскольку у нас уже есть почтовый аккаунт:




3. Добавляем почтовый адрес и жмем кнопку «Продолжить»:




4. Выбираем протокол сбора почты (IMAP) и жмем кнопку «Готово»:




5. На этом шаге проверяем настройки почтового сервера и, если все в порядке, жмем кнопку «Готово»:




6. Вводим авторизационные данные от своей учетной записи в Mail.Ru:




7. Соглашаемся с тем, что Thunderbird будет собирать почту с нашего аккаунта:




8. Ожидаем, когда письма будут скачаны:





4. Заключение


Как видите, мы стараемся развивать не только свои opensource-проекты, но и сторонние. Мы крайне щепетильны в вопросах безопасности, поэтому решили подключиться к разработке Mozilla Thunderbird и помочь с реализацией OAuth 2.0. Надеемся, наш пост воодушевит кого-то сделать свой первый pull request, и мир opensource статет чуточку лучше.


5. Благодарности


  • Kent James — за код-ревью и включение нашего патча в ранний релиз.
  • Andrew Sutherland — за помощь с ISP.

6. Информационные ссылки


  • +32
  • 8,3k
  • 9
Mail.ru Group
1122,00
Строим Интернет
Поделиться публикацией

Похожие публикации

Комментарии 9

  • НЛО прилетело и опубликовало эту надпись здесь
      +3
      На вскидку:
      1) отсутствие необходимости передавать сторонним сервисам логин/пароль;
      2) возможность избирательно отозвать доступ для конкретного стороннего сервиса.
        0
        1) а что за «сторонние сервисы», когда я настраиваю почту у себя в почтовике?
          0
          «У себя в почтовике» = сторонний сервис. У вас есть емейл — это сервис. Вы к нему предоставляете доступ другим, СТОРОННИМ сервисам. Без разницы сколько их — 1 или 100500. И чем меньше сторонних сервисов будет иметь логин/пароль к сервису, на котором они хотят авторизоваться — тем спокойнее на душе у пользователя :).
            0
            Разговор идет про предоставление доступа локальному почтовику к почте mail.ru через OAuth. Объясните, пожалуйста, каким образом он является «сторонним сервисом».
              +1
              Когда локальный клиент сохраняет токен, это лучше, чем когда он сохраняет пароль.
              Пароль, сохраненный на компе может украсть троян.
              Токен тоже может быть украден трояном, но, во-первых, такие трояны менее распространены, во-вторых, в этом случае пострадает только почтовый аккаунт, а в случае, если будет украден пароль — и все остальные аккаунты пользователя с этим паролем.
              А еще, когда юзер авторизуется по IMAP паролем, сервису сложнее отличать настоящего юзера от хакеров и брутфорсеров. При авторизации через OAuth, можно показать юзеру в WebView капчу, спросить дополнительные данные и т.д. Причем, при использовании пароля, IMAP-авторизации с паролем повторяются из раза в раз, и сервис каждый раз должен принимать решение — пользователь это или брутфорсер, при этом имея очень мало информации для принятия такого решения.
      +3
      Стал использовать сразу после появления в релиз-нотисах для аутентификации Thunderbird в GMail и даже подумать не мог, что эта радость — дело рук mail.ru :-)
      Спасибо большое!
        +1

        У меня такое же ложное впечатление сложилось от заголовка. Из текста же статьи оказалось что они только конфигурацию для Mail.ru добавили (если я правильно понял), так что сам OAuth2 в Thunderbird появился раньше.

          +1
          Действительно. Прочитал еще раз статью и не понял, почему я решил, что речь шла не только о добавлении в ISP-базу.
          В общем, всё равно молодцы :-)

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

      Самое читаемое