Некоторое время назад мы рассказывали о том, как в 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. Так мы быстро получили общее представление об основных этапах интеграции:
- Тикет на добавление почтового клиента.
- Тикет на добавление конфигурации в ISP-базу.
- Патч в репозиторий comm-central.
- Патч в ISP-базу.
- Тестовая сборка.
- Сохранение обратной совместимости.
- Тестирование функциональности в ранних релизах.
- Тестирование релиза.
Далее рассмотрим каждый этап в отдельности.
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 with0: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 with0: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
Более подробную информацию о модульном тестировании смотрите ниже по ссылкам:
- Руководство по XPCShell.
- Инструкция по запуску XPCShell-тестов в продукте MailNews.
- Информация о фреймворке для тестирования 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. Информационные ссылки
- Как в Mail.Ru реализован сбор почты с использованием протокола OAuth 2.0
- OAuth 2.0 простым и понятным языком
- Вводная информация по автоматизированному тестированию Thunderbird
- Руководство по XPCShell
- Инструкция по запуску XPCShell-тестов в продукте MailNews
- Тестирование утечек памяти в продукте MailNews
- Информация о фреймворке для тестирования AsyncTestUtils
- Репозиторий comm-central
- Репозиторий ISP
- FTP-сервер Thunderbird
- Treeherder CI
- Руководство по Treeherder CI
- Основная страница клиента Thunderbird
- Полный список релизов Thunderbird
- Ранние релизы Thunderbird
- Багтрекер Bugzilla
- Thunderbird API
- Руководство по сборке Thunderbird
- Документация Thunderbird
- Документация о репозитории comm-central
- Каналы связи разработчиков Thunderbird
- Требования к стилистике написания программного кода
- Информация о релиз-менеджменте Mozilla
- Календарь событий Mozilla
Поделиться с друзьями
Комментарии (9)
kamazee
07.06.2016 22:35+3Стал использовать сразу после появления в релиз-нотисах для аутентификации Thunderbird в GMail и даже подумать не мог, что эта радость — дело рук mail.ru :-)
Спасибо большое!nazarpc
09.06.2016 02:10+1У меня такое же ложное впечатление сложилось от заголовка. Из текста же статьи оказалось что они только конфигурацию для Mail.ru добавили (если я правильно понял), так что сам OAuth2 в Thunderbird появился раньше.
kamazee
10.06.2016 00:38+1Действительно. Прочитал еще раз статью и не понял, почему я решил, что речь шла не только о добавлении в ISP-базу.
В общем, всё равно молодцы :-)
DeLuxis
В чем преимущество OAuth авторизации в почте, перед традиционными способами?
VYBGSS
На вскидку:
1) отсутствие необходимости передавать сторонним сервисам логин/пароль;
2) возможность избирательно отозвать доступ для конкретного стороннего сервиса.
Merlyel
1) а что за «сторонние сервисы», когда я настраиваю почту у себя в почтовике?
VYBGSS
«У себя в почтовике» = сторонний сервис. У вас есть емейл — это сервис. Вы к нему предоставляете доступ другим, СТОРОННИМ сервисам. Без разницы сколько их — 1 или 100500. И чем меньше сторонних сервисов будет иметь логин/пароль к сервису, на котором они хотят авторизоваться — тем спокойнее на душе у пользователя :).
Merlyel
Разговор идет про предоставление доступа локальному почтовику к почте mail.ru через OAuth. Объясните, пожалуйста, каким образом он является «сторонним сервисом».
valievkarim
Когда локальный клиент сохраняет токен, это лучше, чем когда он сохраняет пароль.
Пароль, сохраненный на компе может украсть троян.
Токен тоже может быть украден трояном, но, во-первых, такие трояны менее распространены, во-вторых, в этом случае пострадает только почтовый аккаунт, а в случае, если будет украден пароль — и все остальные аккаунты пользователя с этим паролем.
А еще, когда юзер авторизуется по IMAP паролем, сервису сложнее отличать настоящего юзера от хакеров и брутфорсеров. При авторизации через OAuth, можно показать юзеру в WebView капчу, спросить дополнительные данные и т.д. Причем, при использовании пароля, IMAP-авторизации с паролем повторяются из раза в раз, и сервис каждый раз должен принимать решение — пользователь это или брутфорсер, при этом имея очень мало информации для принятия такого решения.