折腾了好一段时间,office 365、outlook 等的授权验证方式过于麻烦,因此记录开发步骤。
大概开发过程如下:
-
Go to register an Azure account, and then register the application.
-
The tenant invites the user to join the organization.
-
Use the application to obtain user authorization via the OAuth 2.0 link.
-
Obtain the user’s access_token to access user information, send emails on behalf of the user, etc.
但是这些过程并不是一帆风顺,有很多曲折。比如不能跨租户访问等,必须先邀请用户加入组织。
否则会报错:
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.
应用注册
打开:
https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
If you don’t have an account, you can register and log in first; registering for AAD does not require enabling an Azure subscription.
然后新注册一个应用。
- “Supported account types” should select support for any organization directory and personal accounts.
- Choose web application.
- Set the redirect URL for the web service.
注册应用完成后,可以去 “身份验证” 把这些功能都开启一些,没有什么特殊作用,现在开启,免得以后要用时重新配置。
然后创建客户端密码(即密钥),用于 OAuth2.0 访问授权、获取 access_token。
密钥是有有效期的。也可以使用证书,更加安全。
请在创建的时候保存密钥,因为除了第一次可以看到密钥,之后不能再次查看密钥!
然后到 API 权限中添加权限。
勾选以下四个即可。
关于应用的类型,有以下区别。
- Web applications obtain a code via OAuth2.0 redirection and then retrieve the access_token, requiring a redirect URL setup.
- For client applications, a request must first be made to the Microsoft server to generate a device ID, which needs to be filled out.
本文只说 Web 应用。
跨租户或来宾访问
这一步是必须的。因为注册的应用只能访问当前租户/组织内的组织用户、个人用户,无法访问其他个人用户、或其他租户内的用户。因此,必须要邀请用户加入到自己的组织!
这一步要使用的是 Microsoft Entra ID,访问地址:
https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/Overview
点击 “用户”。
邀请外部用户。
身份设置为来宾即可。
然后用户会收到一份电子邮件邀请。
如果不想让用户这么麻烦,你可以在提交邀请后,直接告诉用户打开:https://myapplications.microsoft.com/?tenantid={租户id}
即可。
租户 id 很容易找到。比方在应用的属性界面、其他组织管理界面。一般情况下,租户id、目录id、组织id,是相同的意思。
但是对应要开发的应用来说,这样太麻烦了。可以通过 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。
因为我们要开发程序,因此要使用 “只应用访问”,使用应用方式获取访问权限,去请求租户中的 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
添加 API 权限之后,如果提示需要管理员同意,则点击上方的同意即可。
权限参考: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
scope
、grant_type
值固定即可。

此接口不需要获取 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
。
属性说明如下:
| 属性 | 类型 | 必填 | 说明 |
| :---------------------- | :----------------------------------------------------------- | ---- | :----------------------------------------------------------- |
| invitedUserDisplayName | String | 否 | 被邀请的用户的显示名称。 |
| invitedUserEmailAddress | String | 是 | 被邀请的用户的电子邮件地址。不能使用 ~、!、$ 等特殊字符。 |
| invitedUserMessageInfo | invitedUserMessageInfo | 否 | 包括自定义邮件文本、语言和抄送收件人列表。 |
| invitedUserType | String | 否 | 被邀请的用户的 userType,默认情况下, 来宾为Guest
,成员为Member
。 |
| inviteRedirectUrl | String | 是 | 兑换邀请后,用户应重定向到的 URL。 |
| inviteRedeemUrl | String | 否 | 用户可用于兑换邀请的 URL。 |
| resetRedemption | Boolean | 否 | 重置用户的兑换状态并重新邀请用户,同时保留其用户标识符、组成员身份和应用分配。此属性允许用户使用与上一个邀请中的电子邮件地址不同的电子邮件地址登录。有关使用此属性的详细信息,请参见 重置来宾用户的兑换状态。 |
| sendInvitationMessage | Boolean | 否 | 指示电子邮件是否应发送至邀请的用户。默认值为false
。 |
| status | String | 否 | 邀请的状态。可能的值为:PendingAcceptance
、Completed
、InProgress
和Error
。 |
发送添加用户请求后,在后台的用户列表可以看到此用户。
然后此用户打开 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 等参数。
回跳地址携带了参数:
第二步,后台使用 code 获取该用户的 access_token 。
请求地址:
https://login.microsoftonline.com/{租户id}/oauth2/v2.0/token
Body 类型 : application/x-www-form-urlencoded
。
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..."
}
Regarding the scope, it should be noted that if you want to obtain a refresh_token when fetching an access_token, you need to use the format
openid offline_access https://graph.microsoft.com/mail.Send
. The prefixopenid offline_access
must be used. If this prefix is not set, the refresh_token cannot be obtained.
Next, you can use the API to replace user requests to various interfaces in Outlook. These interfaces are all part of Microsoft Graph.
Microsoft Graph is the gateway to data and intelligence in Microsoft 365. Use Microsoft Graph to build intelligent applications, gain insights, and extend the Microsoft 365 experience.
To use these interfaces, different permissions need to be enabled.
B2B External Identity
Generally, it does not need to be set and is mainly used to handle how external users join and access this organization's resources.
Unable to Access User API
Error reported after the request:
{
"error": {
"code": "MailboxNotEnabledForRESTAPI",
"message": "The mailbox is either inactive, soft-deleted, or is hosted on-premise."
}
}
Documentation reference:
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
This is because some interfaces cannot be used across tenants. Therefore, when applying for user authorization and obtaining access_token, the tenant ID should not be used, but rather common
.
Change the authorization URL to:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={client_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
Change the access_token URL to:
https://login.microsoftonline.com/common/oauth2/v2.0/token
Then use the obtained access_token to access various APIs.
If other interfaces can be used, but only the email sending interface does not work:
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
文章评论