如何在服务/守护程序应用程序中获取 EWS 托管 API 的 OAuth2 访问令牌 [英] How to get OAuth2 access token for EWS managed API in service/daemon application

查看:28
本文介绍了如何在服务/守护程序应用程序中获取 EWS 托管 API 的 OAuth2 访问令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

场景

我在 Azure VM 上有一个 Exchange Online 环境和服务/daemin(无交互用户)应用程序.服务使用 EWS 托管 API 来处理任何租户用户邮箱中的电子邮件.现在 EWS 客户端使用基本身份验证,据 Microsoft 称,EWS 将不支持该身份验证来访问 Exchange Online.

问题/问题

因此,我需要找到一种方法来获取服务/守护程序应用程序的有效访问令牌,以便与 EWS 托管 API 一起使用.

我的发现

以下文章 展示了将 OAuth 2.0 与 EWS 托管 API 结合使用的示例.此示例有效,但它使用了不适合服务/守护程序应用场景,因为没有交互式用户.>

对于服务/守护程序应用程序,我需要使用 client credential 身份验证流程.

注册申请

https://aad.portal.azure.com 门户上使用管理员帐户我注册了应用程序Azure 活动目录.为已注册的应用程序添加了客户端密钥.

上述文章 使用 https://outlook.office.com/EWS.AccessAsUser.All 作为<代码>范围.但是我没有在门户网站上找到这样一个 URL 的许可.我在Office 365 Exchange Online > 应用程序权限 > Mail 下只找到以下权限:

  1. https://outlook.office365.com/Mail.Read 允许应用在没有登录用户的情况下读取所有邮箱中的邮件
  2. https://outlook.office365.com/Mail.ReadWrite 允许应用在没有登录用户的情况下创建、读取、更新和删除所有邮箱中的邮件.

我添加了它们并为所有用户授予管理员许可.

获取访问令牌

出于测试目的和简单起见,我没有使用任何身份验证库(ADAL、MSAL 等).我使用 Postman 获取访问令牌,然后在调试中设置 token 变量(请参阅帖子后面的代码片段).

我尝试了不同的端点来获取访问令牌.

  1. OAuth 2.0 令牌端点 (v2)

 POST:https://login.microsoftonline.com//oauth2/v2.0/tokengrant_type=client_credentialsclient_id=***客户秘密=***scope=https://outlook.office.com/EWS.AccessAsUser.All

发送此请求会产生以下错误响应:

AADSTS70011:提供的请求必须包含范围"输入参数.为输入参数范围"提供的值无效.范围 https://outlook.office.com/EWS.AccessAsUser.All 不是有效.

我尝试将 scope 更改为 https://outlook.office.com/.default.访问令牌已返回,但它似乎对 EWS 无效.EWS 客户端使用以下 x-ms-diagnostics 响应标头值引发 401 错误:

2000008;reason="令牌不包含权限,或者权限无法理解.";error_category="invalid_grant"

  1. OAuth 2.0 令牌端点 (v1)

 POST:https://login.microsoftonline.com//oauth2/tokengrant_type=client_credentialsclient_id=***客户秘密=***资源=https://outlook.office.com

访问令牌已返回,但似乎对 EWS 无效.EWS 客户端抛出 401 错误,其 x-ms-diagnostics 响应标头的值与 #1 中描述的相同.

通过 EWS 托管 API 使用获取的访问令牌

这是我用来使用 Postman 中获取的访问令牌测试 EWS 客户端的代码示例:

var token = "...";var 客户端 = 新的 ExchangeService{Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),凭证 = 新的 OAuthCredentials(token),ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress,"user@domain.onmicrosoft.com"),};var folder = Folder.Bind(client, WellKnownFolderName.SentItems);

解决方案

我们遇到了类似的问题:我们想使用服务帐户连接到单个邮箱,并且只使用 EWS API 做一些事情(例如在GAL) 和 full_access_as_app 似乎有点矫枉过正.幸运的是,这是可能的:

  1. 遵循正常的委托";步骤

  2. 并使用它通过用户名/密码获取令牌:

<代码>...var cred = new NetworkCredential(用户名",密码");var authResult = await pca.AcquireTokenByUsernamePassword(new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" }, cred.UserName, cred.SecurePassword).ExecuteAsync();...

  1. 要完成这项工作,您需要启用将应用程序视为公共客户端"在认证"下>高级设置"因为这使用资源所有者密码凭证流".(这个 SO 答案对我帮助很大!)

通过这种设置,我们可以使用传统"用户名/密码方式,但使用 OAuth 和 EWS API.

Scenario

I have an Exchange Online environment and service/daemin (no interactive user) application on the Azure VM. Service uses EWS managed API to work with emails in the mailbox of any tenant user. Now EWS client uses Basic authentication that, according to Microsoft, will become unsupported in EWS to access Exchange Online.

Question/Issue

So, I need to find a way to get valid access token for service/daemon application to use with EWS managed API.

My findings

The following article shows an example of using OAuth 2.0 with EWS managed API. This example works, but it uses interactive method of getting consent (sign-in form appears allowing user authenticate themselves and grant requested permission to application) that is not suitable for service/daemon app scenario, because there is no interactive user.

For service/daemon application I need to use client credential authentication flow.

Registered application

Using admin account on https://aad.portal.azure.com portal I registered application with Azure Active Directory. Added client secret for registered application.

Aforementioned article uses https://outlook.office.com/EWS.AccessAsUser.All as a scope. But I did not find permission with such a URL on the portal. I found only the following permissions under Office 365 Exchange Online > Application permissions > Mail:

  1. https://outlook.office365.com/Mail.Read Allows the app to read mail in all mailboxes without a signed-in user
  2. https://outlook.office365.com/Mail.ReadWrite Allows the app to create, read, update, and delete mail in all mailboxes without a signed-in user.

I added both of them and granted admin consent for all users.

Getting access token

For testing purposes and simplicity I did not use any auth libraries (ADAL, MSAL etc.). I used Postman to get access token, then set token variable in debug (see code snippet later in the post).

I tried different endpoints to get acess token.

  1. OAuth 2.0 token endpoint (v2)

    POST: https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token
        grant_type=client_credentials
        client_id=*** 
        client_secret=***
        scope=https://outlook.office.com/EWS.AccessAsUser.All

Sending this request produces the following error response:

AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope https://outlook.office.com/EWS.AccessAsUser.All is not valid.

I tried changing scope to https://outlook.office.com/.default. Access token was returned, but it appeared to be invalid for EWS. EWS client throws 401 error with the following value of x-ms-diagnostics response header:

2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"

  1. OAuth 2.0 token endpoint (v1)

    POST: https://login.microsoftonline.com/<TENANT_ID>/oauth2/token
        grant_type=client_credentials
        client_id=*** 
        client_secret=***
        resource=https://outlook.office.com

Access token was returned, but also appeared to be invalid for EWS. EWS client throws 401 error with the same value of x-ms-diagnostics response header as described ealier in #1.

Use aquired access token with EWS managed API

Here is code sample that I used to test EWS client with access token acquired in Postman:

var token = "...";
var client = new ExchangeService
{
    Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),
    Credentials = new OAuthCredentials(token),
    ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress,
                 "user@domain.onmicrosoft.com"),

};
var folder = Folder.Bind(client, WellKnownFolderName.SentItems);

解决方案

We had a similar problem: We wanted to use a Service Account to connect to a single mailbox and just doing some stuff with the EWS API (e.g. searching in the GAL) and the full_access_as_app seems like an overkill. Fortunately it is possible:

  1. Follow the normal "delegate" steps

  2. And use this to get a token via username/password:

...
var cred = new NetworkCredential("UserName", "Password");
var authResult = await pca.AcquireTokenByUsernamePassword(new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" }, cred.UserName, cred.SecurePassword).ExecuteAsync();
...

  1. To make this work you need to enable the "Treat application as public client" under "Authentication" > "Advanced settings" because this uses the "Resource owner password credential flow". (This SO answer helped me alot!)

With that setup we could use a "tradional" username/password way, but using OAuth and the EWS API.

这篇关于如何在服务/守护程序应用程序中获取 EWS 托管 API 的 OAuth2 访问令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆