2025, Oct 20 12:17
exchangelib после миграции на Office 365: как перейти на OAuth и починить ошибку «неверные учетные данные»
После миграции на Outlook в Office 365 exchangelib возвращает «неверные учетные данные». Причина — отключение Basic. Пошаговое решение: переход на OAuth.
Когда интеграция с почтовым ящиком годами работает без сбоев и внезапно начинает возвращать «неверные учетные данные», дело почти никогда не в опечатке. Переезд с URL локального типа на Outlook в Office 365 — как раз такой случай: изменилась сама модель аутентификации.
Контекст: что изменилось и почему сломался код
Доступ ранее осуществлялся по адресу вида https://webmail.domain.it/owa/shared_mailbox@domain.it. После миграции в облако точка входа сместилась на https://outlook.office365.com/mail. Интеграция на Python использовала exchangelib с парой имя пользователя/пароль через Credentials. Применялся кастомный HTTP-адаптер для принудительного прокси, были включены autodiscover и делегированный доступ к общему ящику. До миграции этот же код возвращал валидный объект Account. После миграции те же данные теперь приводят к ошибке «неверные учетные данные».
Отключение autodiscover снимало мгновенную ошибку авторизации, но вместо этого появлялось сообщение о необходимости подождать от сервера.
WARNING:exchangelib.util:Server requested back off until 2025-07-31 08:42:34.617995. Sleeping 9.998383 seconds
Попытки сменить формат имени пользователя и принудительно задать прокси через переменные окружения или другой адаптер результата не дали.
Исходный код, который перестал работать после миграции
class RelayHTTPAdapter(requests.adapters.HTTPAdapter):
    def send(self, req, stream=False, timeout=None, verify=True, cert=None, proxies=None):
        proxies = {
            'http': 'proxy',
            'https': 'proxy',
        }
        return super().send(req, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
def acquire_mailbox():
    try:
        usr = "UT-*****-DEV"
        pwd = "Password"
        BaseProtocol.HTTP_ADAPTER_CLS = RelayHTTPAdapter
        cfg = Configuration(
            server='outlook.office365.com',
            retry_policy=FaultTolerance(max_wait=900),
            credentials=Credentials(usr, pwd)
        )
        return Account(
            'shared_mailbox@domain.it',
            config=cfg,
            autodiscover=True,
            access_type=DELEGATE
        )
    except Exception as exc:
        print(f"Account fetch failed: {exc}...")
        return None
Причина
Базовая аутентификация (username/password) больше не поддерживается на office365.com. Поэтому поток Credentials(user, password), который раньше проходил, теперь отклоняется, даже если значения верны. Предупреждение о back-off при переключении autodiscover — побочный эффект, а не решение.
Решение
Переключите интеграцию на OAuth. Необходимая подготовка и изменения в коде описаны здесь: https://ecederstrand.github.io/exchangelib/#impersonation-oauth-on-office-365
В общих чертах на стороне Python нужно перестать создавать Configuration с Credentials(user, password) и вместо этого передавать объект учетных данных на базе OAuth, как указано в официальном руководстве. Остальная схема — прокси-адаптер, сервер, политика повторов, autodiscover и делегированный доступ — может остаться структурно прежней.
Адаптированный пример с OAuth-учетными данными
class RelayHTTPAdapter(requests.adapters.HTTPAdapter):
    def send(self, req, stream=False, timeout=None, verify=True, cert=None, proxies=None):
        proxies = {
            'http': 'proxy',
            'https': 'proxy',
        }
        return super().send(req, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
def acquire_mailbox_oauth(oauth_creds):
    try:
        BaseProtocol.HTTP_ADAPTER_CLS = RelayHTTPAdapter
        cfg = Configuration(
            server='outlook.office365.com',
            retry_policy=FaultTolerance(max_wait=900),
            credentials=oauth_creds
        )
        return Account(
            'shared_mailbox@domain.it',
            config=cfg,
            autodiscover=True,
            access_type=DELEGATE
        )
    except Exception as exc:
        print(f"Account fetch failed: {exc}...")
        return None
Объект oauth_creds должен быть создан согласно документации exchangelib по ссылке выше.
Почему это важно
Отказ от старых методов аутентификации ломает интеграции незаметно. Сервис может выглядеть идентично — тот же хостнейм, тот же почтовый ящик, — но смена конвейера авторизации делает многолетние скрипты непригодными. Понимание того, что «неверные учетные данные» после миграции — это, скорее, изменение политики, а не опечатка, экономит часы на бесполезной проверке прокси, autodiscover и URL-эндпоинтов.
Выводы
Если ваша интеграция с exchangelib перестала работать после перехода на Outlook в Office 365, и вы по‑прежнему передаете имя пользователя и пароль, в этом и состоит проблема. Замените Basic-аутентификацию на OAuth, следуя официальной инструкции exchangelib. Оставьте логику работы с прокси и конфигурацию как есть; измените только механизм учетных данных. Это приведет интеграцию в соответствие с текущей моделью аутентификации и восстановит доступ к общему почтовому ящику без опоры на устаревшие схемы.
Статья основана на вопросе с StackOverflow от zacthebigkub и ответе Erik Cederstrand.