Microsoft Graph、outlook 授权 Auth2.0 指北

2023年11月7日 1734点热度 0人点赞 2条评论
内容纲要

折腾了好一段时间,office 365、outlook 等的授权验证方式过于麻烦,因此记录开发步骤。

大概开发过程如下:
1,去注册 Azure 账号,然后进行应用注册。

2,租户邀请该用户加入组织中。

3,使用应用通过 OAuth 2.0 链接,获取用户的授权。

4,获取用户的 access_token 访问用户信息、替用户发送邮件等。

但是这些过程并不是一帆风顺,有很多曲折。比如不能跨租户访问等,必须先邀请用户加入组织。
否则会报错:

AADSTS50020: User account 'xxx@xxx.onmicrosoft.com' from identity provider 'https://sts.windows.net/--------/' does not exist in tenant 'Default Directory' and cannot access the application '---------'(My Apps) in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.

file

应用注册

打开:
https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade

如果没有账号可以先注册登录,注册 AAD 不需要开通 Azure 订阅。

然后新注册一个应用。

file

  • “受支持的账号类型” 要选支持任意组织目录和个人。

  • 选择 web 应用。

  • 重定向应用设置 web 服务回跳地址。

file

注册应用完成后,可以去 “身份验证” 把这些功能都开启一些,没有什么特殊作用,现在开启,免得以后要用时重新配置。

file

然后创建客户端密码(即密钥),用于 OAuth2.0 访问授权、获取 access_token。

密钥是有有效期的。也可以使用证书,更加安全。
请在创建的时候保存密钥,因为除了第一次可以看到密钥,之后不能再次查看密钥!

file

然后到 API 权限中添加权限。

file
file

勾选以下四个即可。

file

关于应用的类型,有以下区别。

  • web 应用,通过 OAuth2.0 回跳获取 code,再自行获取 access_token,需要设置回跳地址。
  • 客户端类型,需要先向微软服务器请求,生成设备号,然后要求填写设备号即可。

file

本文只说 Web 应用。

跨租户或来宾访问

这一步是必须的。
因为注册的应用只能访问当前租户/组织内的组织用户、个人用户,无法访问其他个人用户、或其他租户内的用户。
因此,必须要邀请用户加入到自己的组织!

这一步要使用的是 Microsoft Entra ID,访问地址:
https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/Overview

点击 “用户”。

file

邀请外部用户。

file

file

身份设置为来宾即可。

file

然后用户会收到一份电子邮件邀请。

file

如果不想让用户这么麻烦,你可以在提交邀请后,直接告诉用户打开:https://myapplications.microsoft.com/?tenantid={租户id} 即可。

租户 id 很容易找到。 比如在应用的属性界面、其他组织管理界面。一般情况下,租户id、目录id、组织id,是相同的意思。
file

但是对应要开发的应用来说,这样太麻烦了。
可以通过 https://learn.microsoft.com/zh-cn/graph/api/invitation-post?view=graph-rest-1.0&tabs=http 文档说明进行邀请。

如果租户上禁用了 B2B 邀请,或者 B2B 邀请仅限于管理员,则应用程序权限 (仅限应用) 不起作用。
这一点请看文章后面的 B2B 一节。

为了能够通过 API 访问租户的 API,通过 API 请求邀请用户,你需要写一套对接 Microsoft Entra ID 的代码。下一个小节介绍。

请注意,这里跟后面 outlook 授权用户的是不一样的!

API 邀请外部用户

有两种方式通过 API 授权访问 Microsoft Entra ID 。
file

因为我们要开发程序,因此要使用 “只应用访问”,使用应用方式获取访问权限,去请求租户中的 API,不可能通过用户访问授权操作 API。

文档地址:
https://learn.microsoft.com/zh-cn/graph/auth/auth-concepts
https://learn.microsoft.com/zh-cn/graph/auth-v2-service?tabs=http

若要使应用使用客户端凭据流获取对 Microsoft Graph 的授权和访问权限,必须执行以下五个步骤:

  • 使用 Microsoft Entra ID 注册应用。
  • 对应用配置 Microsoft Graph 应用程序权限。
  • 请求管理员同意。
  • 请求访问令牌。
  • 使用访问令牌调用 Microsoft Graph。

打开 应用注册,找到文章开头注册的应用。
添加新的 API 权限。

包括:User.Invite.All

file

添加 API 权限之后,如果提示需要管理员同意,则点击上方的同意即可。

file

权限参考:https://learn.microsoft.com/zh-cn/graph/permissions-reference

如果在后台已经将 API 权限添加到列表中,那么 OAuth2.0 获取 access_token 时,scope 只需要填写 https://graph.microsoft.com/.default 即可。

然后,使用应用id 和密钥即可直接获取 access_token。

请求地址:

https://login.microsoftonline.com/{租户id}/oauth2/v2.0/token

请求方式:

application/x-www-form-urlencoded

请求参数:

client_id:11111111111111111111111111111
scope:https://graph.microsoft.com/.default
client_secret:11111111111111111111111111111
grant_type:client_credentials

scopegrant_type 值固定即可。

![1699326540817](d:\Users\BSI\Documents\WeChat Files\wxid_nxurvoudbu5322\FileStorage\Temp\1699326540817.png)

此接口不需要获取 refresh_token,只要 token 过期,直接重新获取就行,不需要刷新 access_token。

拿到 access_token 后,请求接口邀请用户:

https://graph.microsoft.com/v1.0/invitations

接口请求类型是: application/json

Body:

{
  "invitedUserEmailAddress": "aaa@aaa.onmicrosoft.com",
  "inviteRedirectUrl": "http://localhost:5173",
  "invitedUserType": "Guest",
  "sendInvitationMessage": true
}

Authorization 填写前面获取到的 access_token。

invitedUserType 要填 Member 不要使用 Guest,否则即使获得用户授权后,依然不能替代用户发送邮件。但是 User.Invite.All 权限只能 Invite guest users to the organization

file

属性说明如下:

属性 类型 必填 说明
invitedUserDisplayName String 被邀请的用户的显示名称。
invitedUserEmailAddress String 被邀请的用户的电子邮件地址。 不能使用 ~、!、$ 等特殊字符。
invitedUserMessageInfo invitedUserMessageInfo 包括自定义邮件文本、语言和抄送收件人列表。
invitedUserType String 被邀请的用户的 userType,默认情况下, 来宾为 Guest ,成员为 Member
inviteRedirectUrl String 兑换邀请后,用户应重定向到的 URL。
inviteRedeemUrl String 用户可用于兑换邀请的 URL。
resetRedemption 布尔值 重置用户的兑换状态并重新邀请用户,同时保留其用户标识符、组成员身份和应用分配。 此属性允许用户使用与上一个邀请中的电子邮件地址不同的电子邮件地址登录。 有关使用此属性的详细信息,请参阅 重置来宾用户的兑换状态
sendInvitationMessage Boolean 指示电子邮件是否应发送至邀请的用户。 默认值为 false
status String 邀请的状态。 可能的值为: PendingAcceptanceCompletedInProgressError

file

发送添加用户请求后,在后台的用户列表可以看到此用户。

然后此用户打开 https://myapplications.microsoft.com/?tenantid={租户id} 即可授权应用,或者配置发送邮件,用户点击邮件即可访问。

你可以进一步使用 API 判断此邮件是否已经加入了组织。

如果你想一次性搞完,可以把 inviteRedirectUrl 地址设置成获取用户授权的调整地址。这样当用户加入组织后,立即跳转到用户授权界面,一步到位。

获取用户授权

此步骤前提是,该用户已经接受邀请,并被加入到应用所在的租户/组织中。

此步骤,是标准的 OAuth2.0 用户授权流程,如果是 Web应用,必须设置回调地址。

首先,构建授权的 URL 地址:

https://login.microsoftonline.com/{租户id}/oauth2/v2.0/authorize?client_id={应用的id}&response_type=code&redirect_uri={回调的地址}&response_mode=query&scope=https%3A%2F%2Fgraph.microsoft.com%2FMail.Send&state=1234
  • 回调的地址需要使用 URL 编码,如 http://localhost:5173 编码成 http%3A%2F%2Flocalhost%3A5173

  • scope 也需要 URL 编码,里面的权限项使用空格分隔,注意空格也是 URL 编码。scope 名称可以从 Microsoft Graph rest api 文档中找到。

    地址:https://learn.microsoft.com/zh-cn/graph/overview?view=graph-rest-1.0

  • state 是客户端自己定义的,可以填写时间戳、guid 等标记,用于分辨是哪个用户点击的授权等。

用户打开该 url 后并授权后,会调整到回跳地址,地址中携带 code、state 等参数。

file

回跳地址携带了参数:

file

第二步,后台使用 code 获取该用户的 access_token 。

请求地址:

https://login.microsoftonline.com/{租户id}/oauth2/v2.0/token

Body 类型 : application/x-www-form-urlencoded

file

client_id:11
scope:https://graph.microsoft.com/mail.Send
redirect_uri:http://localhost:5173
code:....
redirect_uri:http://localhost:5173
client_secret:...
grant_type:authorization_code

获取到 access_token,后台可以通过 refresh_token 去定时刷新用户的 access_token,防止 access_token 失效后用户需要重新授权。

HTTP/1.1 200 OK
Content-type: application/json

{
    "token_type": "Bearer",
    "scope": "Mail.Read User.Read",
    "expires_in": 3736,
    "ext_expires_in": 3736,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
    "refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4..."
}

file

关于 socpe ,这里要说明一下。如果想获取 access_token 时,返回 refresh_token,则需要使用 openid offline_access https://graph.microsoft.com/mail.Send 这样的格式。前缀使用 openid offline_access 。如果不设置这个前缀,那么是拿不到 refresh_token 的。

file

接下来,可以使用 API 替代用户,请求 outlook 中各个接口。这些接口统一属于 Microsoft Graph 。

Microsoft Graph 是 Microsoft 365 中通往数据和智能的网关。 使用 Microsoft Graph 生成智能应用、获取见解和分析,并扩展 Microsoft 365 体验。

要使用这些接口,需要开通不同的权限。

file

file

B2B 外部标识

一般不需要设置,主要是用于处理租户外部用户如何加入和访问此组织的资源。

file
file
file

不能访问用户 API

请求后报错:

{
    "error": {
        "code": "MailboxNotEnabledForRESTAPI",
        "message": "The mailbox is either inactive, soft-deleted, or is hosted on-premise."
    }
}

文档说明地址:

https://learn.microsoft.com/en-us/exchange/troubleshoot/user-and-shared-mailboxes/rest-api-is-not-yet-supported-for-this-mailbox-error

https://learn.microsoft.com/zh-cn/graph/outlook-send-mail-from-other-user

https://learn.microsoft.com/en-us/answers/questions/930733/graph-api-send-mail-error-mailboxnotenabledforrest

这是因为部分接口不能跨租户使用,因此申请获取用户授权时以及获取 access_token 时,不能使用租户id获取,而是使用 common。

获取授权的地址改成:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={客户端id}&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A5173&response_mode=query&scope=openid%20offline_access%20email%20Mail.ReadWrite%20Mail.Send.Shared&state=1234

获取 access_token 的地址改成:

https://login.microsoftonline.com/common/oauth2/v2.0/token

然后使用获取到的 access_token 即可访问各种 api。

file

如果其他接口都可以使用,只有发送邮件接口不行:

Remote server returned '550 5.7.708 Service unavailable. Access denied, traffic not accepted from this IP. For more information please go to http://go.microsoft.com/fwlink/?LinkId=526653 AS(7230)

https://learn.microsoft.com/en-us/answers/questions/1154117/your-message-wasnt-delivered-because-the-recipient

https://stackoverflow.com/questions/59679575/microsoft-graph-email-delivery-failure-when-using-sendmail-api

https://learn.microsoft.com/en-us/exchange/troubleshoot/email-delivery/ndr/fix-error-code-5-7-700-through-5-7-750

痴者工良

高级程序员劝退师

文章评论

  • b-yp

    准备给 logseq 开发一个同步 Microsoft ToDo 的插件,结果搞了两晚上没搞懂 Microsoft graph 的授权验证,于是搁置了

    2024年7月10日
    • 痴者工良

      @b-yp 那现在搞好了么

      2024年7月14日