2025, Dec 05 21:02
Как добавить правила Outlook через Microsoft Graph: отказ от ROPC и правильный Authorization Code Flow
Разбираемся, почему ROPC даёт AADSTS50034 для личных Outlook‑аккаунтов и как через Authorization Code Flow получить refresh‑токен и создать правила в Graph.
Когда вы пытаетесь добавить правила почтового ящика Outlook через Microsoft Graph для личных учетных записей Outlook, может возникнуть соблазн напрямую обменять логин и пароль на токен или переиспользовать существующий refresh‑токен от другого клиента. Такой путь часто приводит к ошибке AADSTS50034 и тупиковым ситуациям. Ниже — почему это происходит и как простым способом получить рабочий refresh‑токен и стабильно создавать правила.
Что идет не так
Попытка ниже использует поток Resource Owner Password Credentials (ROPC) для конечной точки токенов, привязанной к конкретному тенанту. Для личной учетной записи Outlook, которая не принадлежит вашему тенанту Azure AD, это завершается ошибкой поиска в каталоге.
Учетная запись пользователя {EUII Hidden} не существует в каталоге a7163cca-35ea-483a-837e-ae68f9820cff. Чтобы войти в это приложение, учетную запись нужно добавить в каталог.
Иными словами, конечная точка токенов, ограниченная конкретным тенантом, аутентифицирует только пользователей этого тенанта. Даже при обращении к /common или /consumers ROPC больше не поддерживается для ряда сценариев, особенно для личных аккаунтов. Значит, вам нужен Authorization Code Flow, чтобы один раз интерактивно получить первый refresh‑токен, а затем автоматизировать процесс.
Демонстрация проблемы
Это шаблон, который вызывает AADSTS50034, когда используется личная учетная запись Outlook, а конечная точка привязана к тенанту.
import requests
def ropc_token_exchange(dir_guid, app_client_id, app_client_secret, user_login, user_pwd):
token_endpoint = f="https://login.microsoftonline.com/{dir_guid}/oauth2/v2.0/token"
form = {
"grant_type": "password",
"client_id": app_client_id,
"client_secret": app_client_secret,
"scope": "https://graph.microsoft.com/.default",
"username": user_login,
"password": user_pwd
}
res = requests.post(token_endpoint, data=form)
return res.json()
reply = ropc_token_exchange(
dir_guid=TENANT_GUID,
app_client_id=APP_ID,
app_client_secret=APP_SECRET,
user_login="JobyTrible235@outlook.com",
user_pwd="5Bfhac7yKB"
)
Ранее нестандартный обмен логина и пароля на конечных точках live.com и common мог давать access‑токен. Этот подход перестал работать и не является поддерживаемым способом получения refresh‑токенов для личных аккаунтов.
Почему это не работает
Личная учетная запись Outlook не является частью вашего тенанта Azure AD, поэтому конечная точка токенов, привязанная к тенанту, не может ее найти и аутентифицировать. Кроме того, ROPC ограничен и не поддерживается для отдельных потоков, в частности для личных аккаунтов. В результате получить refresh‑токен таким способом не получится.
Решение: интерактивный Authorization Code Flow, затем автоматизация
Правильный подход — выполнить однократный интерактивный вход по Authorization Code Flow, чтобы получить исходный refresh‑токен с нужными областями доступа. После этого можно использовать refresh‑токен для получения access‑токенов и вызывать Microsoft Graph для создания правил почтового ящика.
import requests
import webbrowser
import threading
from flask import Flask, request
# Параметры приложения
APP_CLIENT_ID = "client_id_of your_app"
APP_CLIENT_SECRET = "client_secret"
LOCAL_REDIRECT = "http://localhost:8000/callback"
REQ_SCOPES = "offline_access Mail.ReadWrite MailboxSettings.ReadWrite"
authorize_page = (
f="https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
f="?client_id={APP_CLIENT_ID}"
f="&response_type=code"
f="&redirect_uri={LOCAL_REDIRECT}"
f="&response_mode=query"
f="&scope={REQ_SCOPES}"
f="&state=12345"
)
# Локальная конечная точка для перехвата кода авторизации
srv = Flask(__name__)
auth_code_value = None
@srv.route('/callback')
def oauth_return():
global auth_code_value
auth_code_value = request.args.get('code')
return "Authorization code received. You may close this tab."
def start_http():
srv.run(port=8000)
threading.Thread(target=start_http, daemon=True).start()
print("Opening browser for login...")
webbrowser.open(authorize_page)
# Ждем, пока не получим код
while auth_code_value is None:
pass
# Обмениваем код на токены
token_endpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
payload = {
"client_id": APP_CLIENT_ID,
"client_secret": APP_CLIENT_SECRET,
"grant_type": "authorization_code",
"code": auth_code_value,
"redirect_uri": LOCAL_REDIRECT,
"scope": REQ_SCOPES,
}
resp = requests.post(token_endpoint, data=payload)
token_bundle = resp.json()
print("Access Token:", token_bundle.get("access_token", ""))
print("Refresh Token:", token_bundle.get("refresh_token", ""))
# Используем access‑токен для создания правила почтового ящика
access = token_bundle.get("access_token")
if access:
rules_endpoint = "https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messageRules"
headers = {
"Authorization": f"Bearer {access}",
"Content-Type": "application/json"
}
rule_body = {
"displayName": "Test Rule",
"sequence": 1,
"conditions": {
"subjectContains": ["phishing"]
},
"actions": {
"moveToFolder": "junkemail",
"stopProcessingRules": True
},
"isEnabled": True
}
r = requests.post(rules_endpoint, headers=headers, json=rule_body)
print("Rule creation status:", r.status_code)
print("Response:", r.json())
else:
print("Failed to get access token.")
Эта последовательность дает refresh‑токен, который можно переиспользовать для последующих запросов access‑токена, и позволяет вызывать Microsoft Graph для добавления правил почтового ящика.
Почему это важно
Область применимости конечной точки токенов, тип учетной записи и выбор потока тесно связаны в Microsoft Identity. Конечная точка, привязанная к конкретному тенанту, работает только для пользователей этого тенанта. ROPC — непригодный путь для личных аккаунтов. Authorization Code Flow обеспечивает обнаружение учетной записи и согласие с нужными областями, а также возвращает refresh‑токен, необходимый для автоматизации.
Выводы
Для личных учетных записей Outlook избегайте tenant‑scoped ROPC и начинайте с интерактивного Authorization Code Flow. Используйте конечную точку авторизации /common и обменивайте код на /consumers. Запрашивайте конкретные области Microsoft Graph, которые вам нужны, включая offline_access, чтобы получить refresh‑токен. С этим вы сможете программно создавать правила через Microsoft Graph без ошибок каталога или grant_type.