首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通过OAuth2和python库进行Office 365 IMAP身份验证

通过OAuth2和python库进行Office 365 IMAP身份验证
EN

Stack Overflow用户
提问于 2022-09-29 23:39:57
回答 3查看 3.7K关注 0票数 11

我试图升级一个遗留的邮件机器人,使其通过Oauth2进行身份验证,而不是基本的身份验证,因为它是从现在起两天后就被否决了

文档状态应用程序可以保留它们的原始逻辑,同时只交换身份验证位。

构建了使用这些协议发送、读取或以其他方式处理电子邮件的应用程序的应用程序开发人员将能够保持相同的协议,但需要为其用户实现安全的现代身份验证体验。此功能构建在Microsoft Identity platform v2.0之上,并支持访问Microsoft 365电子邮件帐户。

注意,我显式地选择了客户凭证流,因为文档声明

这种类型的授权通常用于必须在后台运行的服务器到服务器之间的交互,而不需要立即与用户进行交互。

因此,我有一个python脚本,它使用MSAL python库检索访问令牌。现在,我正在尝试使用该访问令牌与IMAP服务器进行身份验证。有一些现有的线程显示了如何连接到谷歌,我想我的情况非常接近这一个,除了连接到Office365IMAP服务器之外。这是我的剧本

代码语言:javascript
运行
复制
import imaplib
import msal
import logging

app = msal.ConfidentialClientApplication(
    'client-id',
    authority='https://login.microsoftonline.com/tenant-id',
    client_credential='secret-key'
)

result = app.acquire_token_for_client(scopes=['https://graph.microsoft.com/.default'])

def generate_auth_string(user, token):
  return 'user=%s\1auth=Bearer %s\1\1' % (user, token)

# IMAP time!
mailserver = 'outlook.office365.com'
imapport = 993
M = imaplib.IMAP4_SSL(mailserver,imapport)
M.debug = 4
M.authenticate('XOAUTH2', lambda x: generate_auth_string('user@mydomain.com', result['access_token']))

print(result)

IMAP身份验证失败,尽管设置了M.debug = 4,但输出没有多大帮助。

代码语言:javascript
运行
复制
  22:56.53 > b'DBDH1 AUTHENTICATE XOAUTH2'
  22:56.53 < b'+ '
  22:56.53 write literal size 2048
  22:57.84 < b'DBDH1 NO AUTHENTICATE failed.'
  22:57.84 NO response: b'AUTHENTICATE failed.'
Traceback (most recent call last):
  File "/home/ubuntu/mini-oauth.py", line 21, in <module>
    M.authenticate("XOAUTH2", lambda x: generate_auth_string('user@mydomain.com', result['access_token']))
  File "/usr/lib/python3.10/imaplib.py", line 444, in authenticate
    raise self.error(dat[-1].decode('utf-8', 'replace'))
imaplib.IMAP4.error: AUTHENTICATE failed.

您知道我可能在哪里出错吗?或者如何从IMAP服务器获得更可靠的信息,说明身份验证失败的原因?

的事情我看过

  • 这个答案不再工作,因为建议的作用域无法生成访问令牌。
  • 格兰特看来,客户端凭据流。我不确定这是否包括IMAP资源所需的范围 https://outlook.office.com/IMAP.AccessAsUser.All
  • 验证从Google线程中提取的代码是否正确地生成SASL XOAUTH2字符串(按MS文档上的示例 )。
代码语言:javascript
运行
复制
import base64

user = 'test@contoso.onmicrosoft.com'
token = 'EwBAAl3BAAUFFpUAo7J3Ve0bjLBWZWCclRC3EoAA'

xoauth = "user=%s\1auth=Bearer %s\1\1" % (user, token)

xoauth = xoauth.encode('ascii')
xoauth = base64.b64encode(xoauth)
xoauth = xoauth.decode('ascii')

xsanity = 'dXNlcj10ZXN0QGNvbnRvc28ub25taWNyb3NvZnQuY29tAWF1dGg9QmVhcmVyIEV3QkFBbDNCQUFVRkZwVUFvN0ozVmUwYmpMQldaV0NjbFJDM0VvQUEBAQ=='

print(xoauth == xsanity) # prints True
  • 这个帖子似乎建议需要获取多个令牌,一个用于图形,另一个用于IMAP连接;这是我所缺少的吗?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-10-19 20:06:18

发生imaplib.IMAP4.error: AUTHENTICATE failed错误是因为文档中有一点不太清楚。

通过Powershell设置服务主体时,需要输入App和对象ID。许多人会认为,这是您在注册应用程序的概述页面上看到的对象ID,但它不是!此时,您需要"Azure Active Directory -> Enterprise应用程序-> you -App-> Object-ID"中的对象ID

代码语言:javascript
运行
复制
New-ServicePrincipal -AppId <APPLICATION_ID> -ServiceId <OBJECT_ID> [-Organization <ORGANIZATION_ID>]

微软说:

OBJECT_ID是用于应用程序注册的企业应用程序节点(Azure )概述页面中的对象ID。它不是App注册节点概述中的对象ID。使用不正确的对象ID将导致身份验证失败。

当然,您需要注意API-权限和其他东西,但这对我来说是重点。因此,让我们再一次处理它,就像文档页面中解释的那样。使用OAuth验证IMAP、POP或SMTP连接

  1. 在租户中注册应用程序
  2. 设置应用程序的客户端密钥。
  3. 设置API权限,选择“我的组织使用的API”选项卡,然后搜索“Office365ExchangeOnline”->应用程序权限->选择IMAP和IMAP.AccessAsApp
  4. 在邮箱上设置应用程序的服务主体和完全访问权限
  5. 检查是否为邮箱激活了IMAP

这就是我用来测试它的代码:

代码语言:javascript
运行
复制
import imaplib
import msal
import pprint

conf = {
    "authority": "https://login.microsoftonline.com/XXXXyourtenantIDXXXXX",
    "client_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX", #AppID
    "scope": ['https://outlook.office365.com/.default'],
    "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", #Key-Value
    "secret-id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", #Key-ID
}
    
def generate_auth_string(user, token):
    return f"user={user}\x01auth=Bearer {token}\x01\x01"    

if __name__ == "__main__":
    app = msal.ConfidentialClientApplication(conf['client_id'], authority=conf['authority'],
                                             client_credential=conf['secret'])

    result = app.acquire_token_silent(conf['scope'], account=None)

    if not result:
        print("No suitable token in cache.  Get new one.")
        result = app.acquire_token_for_client(scopes=conf['scope'])

    if "access_token" in result:
        print(result['token_type'])
        pprint.pprint(result)
    else:
        print(result.get("error"))
        print(result.get("error_description"))
        print(result.get("correlation_id"))
        
    imap = imaplib.IMAP4('outlook.office365.com')
    imap.starttls()
    imap.authenticate("XOAUTH2", lambda x: generate_auth_string("target_mailbox@example.com", result['access_token']).encode("utf-8"))

设置服务主体并让应用程序完全访问邮箱后,等待15-30分钟才能使更改生效并进行测试。

票数 1
EN

Stack Overflow用户

发布于 2022-10-12 09:08:50

试试下面的步骤。

对于客户端凭据流,您需要在应用程序注册中分配“应用程序权限”,而不是“委托权限”。

  1. 添加权限“Office365Exchange Online /IMAP.AccessAsApp”(应用程序)。

  1. 同意你的申请
  2. 服务负责人和交易所。
  3. 一旦服务主体在Exchange Online中注册,管理员就可以运行Add权限cmdlet将接收权限分配给服务主体。
  4. 使用范围'https://outlook.office365.com/.default'

现在,您可以通过将该访问令牌和邮箱用户名组合起来使用IMAP4进行身份验证来生成SALS身份验证字符串。

#Python代码

代码语言:javascript
运行
复制
def get_access_token():
    tenantID = 'abc’
    authority = 'https://login.microsoftonline.com/' + tenantID
    clientID = 'abc’
    clientSecret = 'abc
    scope = ['https://outlook.office365.com/.default']
    app = ConfidentialClientApplication(clientID, 
          authority=authority, 
          client_credential = clientSecret)
    access_token = app.acquire_token_for_client(scopes=scope)
    return access_token

 def generate_auth_string(user, token):
    auth_string = f"user={user}\1auth=Bearer {token}\1\1"
    return auth_string

#IMAP AUTHENTICATE
 imap = imaplib.IMAP4_SSL(imap_host, 993)
 imap.debug = 4
 access_token = get_access_token_to_authenticate_imap()
 imap.authenticate("XOAUTH2", lambda x:generate_auth_string(
      'useremail',
       access_token['access_token']))
 imap.select('inbox')
票数 5
EN

Stack Overflow用户

发布于 2022-09-30 09:28:41

尝试使用以下脚本:

代码语言:javascript
运行
复制
import json
import msal

import requests

client_id = '***'
client_secret = '***'
tenant_id = '***'
authority = f"https://login.microsoftonline.com/{tenant_id}"

app = msal.ConfidentialClientApplication(
    client_id=client_id,
    client_credential=client_secret,
    authority=authority)

scopes = ["https://graph.microsoft.com/.default"]

result = None
result = app.acquire_token_silent(scopes, account=None)

if not result:
    print(
        "No suitable token exists in cache. Let's get a new one from Azure Active Directory.")
    result = app.acquire_token_for_client(scopes=scopes)

# if "access_token" in result:
#     print("Access token is " + result["access_token"])


if "access_token" in result:
    userId = "***"
    endpoint = f'https://graph.microsoft.com/v1.0/users/{userId}/sendMail'
    toUserEmail = "***"
    email_msg = {'Message': {'Subject': "Test Sending Email from Python",
                             'Body': {'ContentType': 'Text', 'Content': "This is a test email."},
                             'ToRecipients': [{'EmailAddress': {'Address': toUserEmail}}]
                             },
                 'SaveToSentItems': 'true'}
    r = requests.post(endpoint,
                      headers={'Authorization': 'Bearer ' + result['access_token']}, json=email_msg)
    if r.ok:
        print('Sent email successfully')
    else:
        print(r.json())
else:
    print(result.get("error"))
    print(result.get("error_description"))
    print(result.get("correlation_id"))

来源:https://kontext.tech/article/795/python-send-email-via-microsoft-graph-api

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73902642

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档