了解如何一起使用 OAuth 身份验证和 EWS 托管 API 应用程序。
可以使用 Microsoft Entra 提供的 OAuth 身份验证服务,使 EWS 托管 API 应用程序能够访问 Office 365 中的Exchange Online。 若要将 OAuth 用于你的应用程序,需要执行以下操作:
- 向 Microsoft Entra 注册应用程序。
- 添加代码以获取身份验证令牌,以获取来自令牌服务器的身份验证令牌。
- 对你发送的 EWS 请求添加身份验证令牌。
注意
对 EWS 的 OAuth 身份验证仅在作为 Microsoft 365 一部分的 Exchange Online 中可用。 使用 OAuth 的 EWS 应用程序必须注册到 Microsoft Entra。
若要使用本文中的代码,你需要以下访问权限:
- 具有 Exchange Online 邮箱的 Office 365 帐户。 如果没有 Office 365 帐户,可 注册 Office 365 开发人员计划,以获取免费的 Office 365 订阅。
- .NET 的 Microsoft 身份验证库。
- EWS Managed API。
可以用于访问 Exchange Online 中的 EWS API 的 OAuth 权限类型分为两种。 继续教程前,需要选择要使用的特定权限类型。
- 委派权限供已有用户登录的应用使用。 对于这些应用,用户或管理员同意应用请求的权限,并且应用可在调用 API 时充当已登录的用户。
- 应用程序权限由无需用户登录即可运行的应用使用;例如,作为后台服务或守护程序运行的应用,并且可以访问多个邮箱。
注册应用程序
若要使用 OAuth,应用程序必须具有由 Microsoft Entra 颁发的应用程序 ID。 在本教程中,假定应用程序是控制台应用程序,因此需要使用 Microsoft Entra 将应用程序注册为公共客户端。 可以在 Microsoft Entra 管理中心或使用 Microsoft Graph 注册应用程序。
打开浏览器,导航到Microsoft Entra 管理中心并使用工作或学校帐户登录。
在左侧导航栏中选择“标识”,然后选择“应用程序”下的“应用注册”。
选择“新注册”。 在“注册应用”页上,按如下方式设置值。
- 将 名称 设置为应用的友好名称。
- 将 受支持的帐户类型 设置为对你的方案有意义的选择。
- 若要 重定向 URI,请将下拉列表更改为 公共客户端(移动 & 桌面) 并将值设置为 “
https://login.microsoftonline.com/common/oauth2/nativeclient”。
选择“注册”。 在下一页,复制“应用(客户端)ID”和目录(租户)ID的值,然后保存,你将在下一步中用到它们。
注意
开发人员可以使用工作或学校帐户登录,以在 Entra ID 目录中注册应用,或使用 Entra ID 目录中的来宾个人帐户 (MSA) 登录。 如果开发人员没有 Entra ID 目录,他们可以从 M365 开发人员计划免费获取一个。
配置以进行委派身份验证
如果应用程序使用委派的身份验证,则无需进行进一步配置。 Microsoft 标识平台 允许应用动态请求权限,因此无需预配置应用注册的权限。 但是,在某些情况下(例如 流代表)预配置权限是必需的。 使用以下步骤预配置 EWS 权限。
在“管理”下的左侧导航中,选择 “清单”。
找到清单中的“
requiredResourceAccess”属性,然后在方括号内添加以下内容([]):{ "resourceAppId": "00000002-0000-0ff1-ce00-000000000000", "resourceAccess": [ { "id": "3b5f3d61-589b-4a3c-a359-5dd4b5ee5bd5", "type": "Scope" } ] }选择“保存”。
在“管理”下选择 “API 权限”。 确认列出了 EWS.AccessAsUser.All 权限。
配置仅应用身份验证
若要使用“应用程序权限”,请执行以下额外步骤。
在“管理”下的左侧导航中,选择 “清单”。
找到清单中的“
requiredResourceAccess”属性,然后在方括号内添加以下内容([]):{ "resourceAppId": "00000002-0000-0ff1-ce00-000000000000", "resourceAccess": [ { "id": "dc890d15-9560-4a4c-9b7f-a736ec74ec40", "type": "Role" } ] }选择“保存”。
在“管理”下选择 “API 权限”。 确认列出了 full_access_as_app 权限。
选择“对组织授予管理员许可”,并接受许可对话框。
在“管理”下的左侧导航中,选择“证书 & 机密”。
选择“新建客户端机密”,并输入简短说明,然后选择“添加”。
复制新添加客户端机密的“值”并保存,你等下会用到它。
添加代码以获取身份验证令牌
以下的代码片段显示了如何使用 Microsoft 身份验证库获取代理权限和应用程序权限的身份验证令牌。 这些片段假定执行身份验证请求所需的信息都存储在了应用程序的 App.config 文件中。 这些示例不包括错误检查,请参阅“代码示例”,查看完整代码。
获取委派身份验证的令牌
// Using Microsoft.Identity.Client 4.22.0
// Configure the MSAL client to get tokens
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = ConfigurationManager.AppSettings["appId"],
TenantId = ConfigurationManager.AppSettings["tenantId"]
};
var pca = PublicClientApplicationBuilder
.CreateWithApplicationOptions(pcaOptions).Build();
// The permission scope required for EWS access
var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };
// Make the interactive token request
var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();
获取仅限应用的身份验证令牌
// Using Microsoft.Identity.Client 4.22.0
var cca = ConfidentialClientApplicationBuilder
.Create(ConfigurationManager.AppSettings["appId"])
.WithClientSecret(ConfigurationManager.AppSettings["clientSecret"])
.WithTenantId(ConfigurationManager.AppSettings["tenantId"])
.Build();
// The permission scope required for EWS access
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
//Make the token request
var authResult = await cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();
向 EWS 请求添加身份验证令牌
收到 AuthenticationResult 对象后,可以使用 AccessToken 属性来获取令牌服务发出的令牌。
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
若要使用“应用程序权限”,还需要显式模拟想要访问的邮箱。
//Impersonate the mailbox you'd like to access.
ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "test@demotenant.onmicrosoft.com");
代码示例
委派身份验证
下面是完整的代码示例,该示例演示了如何使用“委派身份验证”,构建被 OAuth 身份验证通过的 EWS 请求。
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Identity.Client;
using System;
using System.Configuration;
namespace EwsOAuth
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
// Using Microsoft.Identity.Client 4.22.0
// Configure the MSAL client to get tokens
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = ConfigurationManager.AppSettings["appId"],
TenantId = ConfigurationManager.AppSettings["tenantId"]
};
var pca = PublicClientApplicationBuilder
.CreateWithApplicationOptions(pcaOptions).Build();
// The permission scope required for EWS access
var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };
try
{
// Make the interactive token request
var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach(var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
}
}
仅限应用的身份验证
仅限应用的身份验证”,构建被 OAuth 身份验证通过的 EWS 请求。
注意
使用模拟时,必须始终使用 X-AnchorMailbox 请求页眉,该页眉应设置为模拟邮箱的 SMTP 地址。
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Identity.Client;
using System;
using System.Configuration;
namespace EwsOAuth
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
// Using Microsoft.Identity.Client 4.22.0
var cca = ConfidentialClientApplicationBuilder
.Create(ConfigurationManager.AppSettings["appId"])
.WithClientSecret(ConfigurationManager.AppSettings["clientSecret"])
.WithTenantId(ConfigurationManager.AppSettings["tenantId"])
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
var authResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "meganb@contoso.onmicrosoft.com");
//Include x-anchormailbox header
ewsClient.HttpHeaders.Add("X-AnchorMailbox", "meganb@contoso.onmicrosoft.com");
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach(var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
}
}
两种情况下的示例代码都需要 App.config 文件,其中包含以下条目:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<appSettings>
<!-- The application ID from your app registration -->
<add key="appId" value="YOUR_APP_ID_HERE" />
<!-- The tenant ID copied from your app registration -->
<add key="tenantId" value="YOUR_TENANT_ID_HERE"/>
<!-- The application's client secret from your app registration. Needed for application permission access -->
<add key="clientSecret" value="YOUR_CLIENT_SECRET_HERE"/>
</appSettings>
</configuration>