我的应用程序在一段时间后出现 401 错误(我相信是由于令牌过期)。我的 Exchange 连接代码是:
每次用户执行与 EWS 相关的任何操作(即在 CRM 程序中选择电子邮件)时,都会调用 Connect。
Public Shared Function Connect() As ExchangeService
app = PublicClientApplicationBuilder.Create(ClientID).WithAuthority(Authority).WithRedirectUri(RedirectUri).Build()
' Authenticate the user and get the ExchangeService object
Dim ewsService As ExchangeService = InitializeEwsService()
' Return the initialized ExchangeService object
Return ewsService
End Function
初始化Ews服务:
Private Shared Function InitializeEwsService() As ExchangeService
Authenticate() ' Ensure authentication before initializing EWS
Dim ewsService As New ExchangeService(ExchangeVersion.Exchange2013)
ewsService.Url = New Uri(EwsUrl)
ewsService.Credentials = New OAuthCredentials(authenticationResult.AccessToken)
Return ewsService
End Function
Private Shared Sub Authenticate()
Try
' Check if we already have a valid token
If authenticationResult Is Nothing OrElse authenticationResult.ExpiresOn.UtcDateTime <= DateTime.UtcNow Then
' Attempt silent authentication
Try
authenticationResult = app.AcquireTokenSilent(Scopes, authenticationResult.Account).ExecuteAsync().Result
Catch ex As MsalUiRequiredException
' Silent authentication failed, fallback to interactive login
Try
authenticationResult = app.AcquireTokenInteractive(Scopes).ExecuteAsync().Result
Catch interactiveEx As Exception
' Handle authentication error
Throw New Exception("Interactive authentication failed: " & interactiveEx.Message)
End Try
Catch ex As Exception
' Silent authentication failed, fallback to interactive login
authenticationResult = app.AcquireTokenInteractive(Scopes).ExecuteAsync().Result
End Try
End If
Catch ex As AggregateException
' Handle authentication error
Throw New Exception("Authentication failed: " & ex.InnerException.Message)
End Try
End Sub
我什至尝试过对所有内容进行粗略重置,当触发 emailRead 代码时,它会捕获 401 错误并运行 Handle401 函数来清除所有内容,但即使在登录后,它也会陷入 401Handle 错误的循环中。
我还注意到登录是不同的,当应用程序首次加载时,用户必须输入密码,然后执行 MFA,大约一个小时后,用户确实会出现登录窗口,但他们单击电子邮件并且永远不会提示输入密码,直接导致 401 错误,就好像过期的令牌永远不会被刷新,但我不知道我错过了什么?
如果您想要续订令牌,则需要使用 MSAL 令牌缓存,请参阅 https://learn.microsoft.com/en-us/entra/msal/dotnet/acquiring-tokens/acquire-token-silently 所以这个
app = PublicClientApplicationBuilder.Create(ClientID).WithAuthority(Authority).WithRedirectUri(RedirectUri).Build()
只应创建一次,否则您将没有缓存可供使用,为每个 EWS 请求执行 InitializeEwsService 都可以,但在这种情况下我会这样做
If authenticationResult Is Nothing OrElse authenticationResult.ExpiresOn.UtcDateTime <= DateTime.UtcNow Then
这不是必需的,因为当您调用 app.AcquireTokenSilent 时,MSAL 将处理令牌过期和续订,如果令牌不存在,它将获取一个新令牌;如果存在,则使用缓存的令牌;或者从刷新令牌续订缓存的令牌如果过期了。
EWS 托管 API 的一个问题是它不提供身份验证回调,每次重新创建 ExchangeService 应该可以工作,但如果您这样做,我建议您让 Authenticate 返回访问令牌,而不是在authenticationResult 的范围内调用 AcquireToken应该只是功能级别。
您可以修改 EWS 托管 API 的源,以使 ExchangeService 回调每次都检查 MSAL 令牌缓存,这使您的代码更加高效和可读,请参阅 https://github.com/gscales/EWS-BasicToOAuth -Info/blob/main/EWA%20Managed%20API%20MASL%20Token%20Refresh.md