【问题标题】:How to authenticate with OAuth to access EWS APIs如何使用 OAuth 进行身份验证以访问 EWS API
【发布时间】:2017-07-31 12:10:24
【问题描述】:

我的网络服务目前正在进行基本的用户名/密码验证,以便订阅交换用户以接收如下事件(如新邮件事件等):

var service = new ExchangeService(exchangeVersion)
                                  {
                                      KeepAlive = true,
                                      Url = new Uri("some autodiscovery url"),
                                      Credentials = new NetworkCredential(username, password)
                                  };

var subscription = service.SubscribeToPushNotifications(
                                    new[] { inboxFolderFoldeID },
                                    new Uri("some post back url"),
                                    15,
                                    null,
                                    EventType.NewMail,
                                    EventType.Created,
                                    EventType.Deleted,
                                    EventType.Modified,
                                    EventType.Moved,
                                    EventType.Copied);

现在,我应该替换身份验证机制以使用 OAuth 协议。我看到了一些示例,但所有示例似乎都在谈论对客户端进行身份验证(https://msdn.microsoft.com/en-us/library/office/dn903761%28v=exchg.150%29.aspx?f=255&MSPPError=-2147217396),但我无法找到如何使用 OAuth 协议对交换用户进行身份验证的示例。任何代码示例都会有很大帮助。谢谢。

【问题讨论】:

  • 在调查 EWS 的 oauth 时出现的另一件事是,它仅适用于 Office 365,不适用于 Exchange 服务器。 msdn.microsoft.com/en-us/library/office/… 表示“EWS 的 OAuth 身份验证仅作为 Office 365 的一部分在 Exchange 中可用。EWS 应用程序需要“完全访问用户邮箱”权限。”
  • 不幸的是,没有一个链接谈到如何让它与 EWS 托管 API 一起工作。我在问题中发布的 msdn url 似乎没有对用户进行身份验证,而只是授权客户端似乎
  • 是的,但这似乎基于 REST API,如果可能,我想使用 EWS 托管 API。但如果不是,我想这将是我最后的选择。

标签: c# oauth exchangewebservices


【解决方案1】:

不清楚您对“网络服务”的含义以及您目前如何获取用户名和密码。如果这是用户需要登录或传递凭据的某种网站,那么您必须从浏览器启动 OAuth2 授权,就像将客户端浏览器重定向到授权端点以启动 implicit grantcode grant。用户将在 OAuth2 服务器(而不是您的应用程序)上看到登录屏幕,一旦用户登录代码或访问令牌(取决于授权)将返回到您的应用程序,您可以在 @987654332 中使用@构造函数。

如果该“网络”服务是在用户计算机上运行的某些服务,您可以使用下述方法之一。

使用 AuthenticationContext 获取 AccessToken

该示例似乎基于旧版本的 AuthenticationContext 类。

other version 似乎更新了,AcquireToken 现在重命名为 AcquireTokenAsync / AcquireTokenSilentAsync

无论您使用哪个版本,您都无法像在当前代码中那样传递用户名和密码。但是,您可以让AcquireToken[Async] 方法提示用户输入凭据。老实说,这比让您的应用程序直接处理这些用户机密更安全。在不知不觉中,您将在数据库中存储纯文本密码(希望您还没有这样做)。

在这两个版本中,这些方法都有很多重载,它们都有不同的参数和略有不同的功能。对于您的用例,我认为这些很有趣:

自动提示行为,在这两个版本中,意味着:当用户尚未缓存时,他们将被要求提供凭据。两个AuthenticationContext 构造函数都允许你传递一个令牌缓存,这是你可以自己实现的东西。在内存、文件或数据库中缓存令牌(请参阅this article 以获取示例文件缓存实现)。

手动获取 AccessToken

如果您真的想在不提示用户的情况下从代码中传递用户凭据,总有办法解决。在这种情况下,您必须按照 OAuth2 规范/RFC6749 中的说明实现Resource Owner Password Credentials grant

巧合与否,我有一个名为 oauth2-client-handler 的开源库,它实现了这个以与 HttpClient 一起使用,但无论如何,如果你想走这条路,你可以深入研究该代码,尤其是从 @987654330 开始@。

使用访问令牌

获得访问令牌后,您可以继续使用this MSDN page 上的示例,例如:

var service = new ExchangeService(exchangeVersion)
                  {
                      KeepAlive = true,
                      Url = new Uri("some autodiscovery url"),
                      Credentials = new OAuthCredentials(authenticationResult.AccessToken))
                  };

【讨论】:

  • 这回答了我的很多问题,谢谢:) 但问题是,为了摆脱处理用户密码,我们想要实现 oauth(这就是我对 oauth 的理解),那么是否可以完全取消用户的用户名和密码身份验证并使用访问令牌对用户进行身份验证?我阅读了一些与模拟用户相关的内容,但没有得到符合我要求的良好示例代码。
  • 另外,ExchangeService 对象似乎有一个名为 ImpersonatedUserId 的属性,这是否意味着我可以模拟该特定用户并为他订阅通知。我猜不是,只是想确定:)
  • 感谢您的编辑,实际上我的应用程序是一个带有一些端点(没有任何 UI)的 Web API,来自某些电子邮件客户端的用户将向其传递他的凭据(这是当前流程),但是,如果可能,我们希望消除他传递密码的需要。
  • 不知道 ImpersonatedUserId 是如何工作的,但我想如果你有足够的访问权限,它可以用来冒充不同的用户。在另一种情况下,您必须启动 OAuth2 授权才能获得访问令牌。根据身份提供者的不同,您可能已经有一个可以使用的 OWIN 身份验证中间件。
  • 好的,我已经提供了“通过 Exchange Web 服务以登录用户身份访问邮箱”的应用程序权限,所以应该允许它冒充用户吗?我会尝试设置 ImpersonatedUserId 并查看是否能够在不传递用户名和密码的情况下订阅用户。谢谢
【解决方案2】:

以防有人仍在努力使其正常工作。我们需要在 Azure 门户上为应用程序上传证书清单,然后使用相同的证书对客户端进行身份验证以获取访问令牌。更多详情请查看:https://blogs.msdn.microsoft.com/exchangedev/2015/01/21/building-daemon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow/

【讨论】:

【解决方案3】:

使用Microsoft Document 中的示例代码作为起点和这些库:

  • Microsoft 身份客户端 4.27
  • EWS 托管 API v2.2

我能够成功通过 Office 365 上的 Exchange 进行身份验证和连接。

      public void Connect_OAuth()
      {
         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" };

         AuthenticationResult authResult = null;

         try
         {
            authResult = cca.AcquireTokenForClient( ewsScopes ).ExecuteAsync().Result;
         }
         catch( Exception ex )
         {
            Console.WriteLine( "Error: " + ex );
         }

         try
         {
            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, "ccc@pppsystems.co.uk" );

            ewsClient.HttpHeaders.Add( "X-AnchorMailbox", "ccc@pppsystems.co.uk" );


            var folders = ewsClient.FindFolders( WellKnownFolderName.MsgFolderRoot, new FolderView( 10 ) );

            foreach( var folder in folders )
            {
               Console.WriteLine( "" + folder.DisplayName );
            }
         }
         catch( Exception ex )
         {
            Console.WriteLine( "Error: " + ex );
         }
      }

Microsoft 示例代码不起作用 - 对 AcquireTokenForClient 的异步调用从未返回。

通过在单独的 try catch 块中调用 AcquireTokenForClient 来捕获一般的 Exception,删除 await 并使用 .Result,现在可以正常工作 - 没有其他任何更改。

我意识到这不是最佳实践,但是无论有没有调试器,原始代码中的异步调用都不会返回。

在 Azure 设置中:

  • 使用了客户端机密文本字符串 - 不需要 x509 证书
  • 配置为“仅应用身份验证”

希望这可以帮助人们避免数小时的挫败感。

【讨论】:

  • 我喜欢你的回答。我想知道你是否帮助我解决我的问题。我在我的应用程序中使用委托身份验证,它是使用 .net 框架 4.5 和 VB.NET 作为语言的 Asp.net Webform。我可以在 Visual Studio 中运行它,但是当我在 IIS 中部署时出现错误:“当应用程序未在 UserInteractive 模式下运行时显示模式/表单无效”。更多详情:stackoverflow.com/questions/67429701/…
  • @AvishekhBharati - 您的问题可能与此有关。从上面的答案中,对于 MSAL、.NET 等的某些组合(可能与版本相关),存在不返回身份验证令牌的多线程问题。我实际上所做的是将异步调用(非阻塞)变为同步调用(阻塞)。查看您的问题 1)您使用的是相当早期的 .NET 版本 2)您仍然收到异步调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
  • 2020-09-26
  • 1970-01-01
  • 2012-11-27
  • 2017-10-05
  • 2012-02-12
相关资源
最近更新 更多