通过EWS托管API与Exchange Online通信时刷新访问令牌

时间:2017-07-17 19:26:46

标签: c# oauth-2.0 exchangewebservices adal ews-managed-api

我正在编写一个应用程序,使用EWS托管API与Exchange Online进行通信,并使用ADAL库通过OAuth 2.0对我的应用程序进行身份验证。

访问令牌在60分钟后到期。之后我需要刷新访问令牌。目前,我在StreamSubscriptionConnection OnNotificationEvent处理程序中执行此操作,以及使用以下代码刷新OAuth访问令牌的OnDisconnect事件处理程序。

private void OnNotificationEventHandler(object sender, NotificationEventArgs args)
{
    exchangeService.Credentials = new OAuthCredentials(GetOAuthAccessToken().Result);

    // Do my work
}

我还在OnDisconnect事件处理程序中添加了相同的刷新访问令牌代码,因为StreamSubscriptionConnection最多只能保持打开30分钟。

private void OnDisconnectEventHandler(object sender, SubscriptionErrorEventArgs args)
{
    exchangeService.Credentials = new OAuthCredentials(GetOAuthAccessToken().Result);
    streamingSubscriptionConnection.Open();
}

以下是我对访问令牌的代码。

private async Task<string> GetOAuthAccessToken(PromptBehavior promptBehavior = PromptBehavior.Auto)
{
    var authenticationContext = new AuthenticationContext(myAadTenant);

    var authenticationResult = await authenticationContext.AcquireTokenAsync(exchangeOnlineServerName, myClientId, redirectUri, new PlatformParameters(promptBehavior));

    return authenticationResult.AccessToken;
}

即使考虑到上述方法&#34;工作&#34;,我觉得这不是处理这种情况的最佳方式,因为我几乎需要确保每当我与之沟通时刷新访问令牌EWS。如果我添加另一个事件处理程序而忘记在事件处理程序中添加令牌刷新逻辑,如果我的访问令牌过期,我可能会在处理该事件时获得401,但我需要在事件处理程序中调用EWS。

上面的代码是简化的,我可以在每次与EWS通信时使用try catch,如果我得到401,我会刷新访问令牌并再试一次,但这并不能解决我上面提到的不便。

我认为应该有一种更简单的方法来处理这个问题,但我还没找到合适的文件。这是我在开发过程中提到的材料。 https://blogs.msdn.microsoft.com/webdav_101/2015/05/11/best-practices-ews-authentication-and-access-issues/

2 个答案:

答案 0 :(得分:1)

另一种方法是,当您通过EWS托管API在线与Exchange通信时,需要提供exchangeService对象。并且您需要为每个请求捕获401异常,并且在获得此异常后,您需要重新设置Credentials对象的exchangeService属性或重新创建此对象。

答案 1 :(得分:0)

我同意应该有一种更好的方式来处理令牌刷新。如果EWS API本身可以管理令牌刷新,那将是很好的选择,但是作为一种变通办法,我做了。.

将对EWS服务的引用放入公共/内部属性中,该属性可以a)如果尚未实例化该服务,则实例化该服务; b)确保身份验证令牌仍然有效(如果无效,则执行令牌刷新) )。然后,我们需要确保该属性是EWS服务的单个访问点。

从广义上看,

public class Mailbox 
{
    private ExchangeService exchangeService;

    public ExchangeService ExchangeService
    {
        get
        {
            if (this.exchangeService == null)
            {
                // Initialise the service
                CreateExchangeService();
            }
            else
            {
                // Ensure token is still valid
                ValidateAuthentication();
            }

            return this.exchangeService;
        }
    }
}

我不会详细介绍CreateExchangeService,但是ValidateAuthentication是对EWS的基本调用,如果未经身份验证,它将引发异常

private void ValidateAuthentication()
{
    try
    {
        Folder inbox = Folder.Bind(this.exchangeService, WellKnownFolderName.Inbox);
    }
    catch //(WebException webEx) when (((HttpWebResponse)webEx.Response).StatusCode == HttpStatusCode.Unauthorized)
    {
        RefreshOAuthCredentials();
    }
}

RefreshOAuthCredentials将仅刷新令牌。同样,我在StreamingSubscription上也有一个OnDisconnect事件处理程序,它将尝试重新打开连接(如果重新打开失败,则重新进行身份验证)。为了简洁起见,这里都没有显示。

尽管要点是,我们现在对EWS服务有一个引用,该引用在每次调用之前执行身份验证检查和重新身份验证(如果需要)。例如,绑定到电子邮件看起来像..

var mailMessage = EmailMessage.Bind(this.mailbox.ExchangeService, itemId);

毫无疑问,这会增加流量/延迟,如果有更好的方法可以避免。而且,如果有人有更好的方法-我很高兴!