使用 OAuth 对 EWS 应用程序进行身份验证
了解如何一起使用 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>