【发布时间】:2020-01-09 14:18:32
【问题描述】:
我的任务是通过将多个 WebForms 应用程序迁移到 MSAL v4 来修改它们。我从 GitHub 下载了一个有效的 MVC C# 示例 (msgraph-training-aspnetmvcapp),它运行完美。我已经成功地模拟了 MVC 示例,直到初始令牌缓存为止。 OWIN单租户登录流程按预期执行;但是,分配给处理通知接收 (OnAuthorizationCodeReceivedAsync) 的 Task 永远不会被解雇。因此,没有令牌放入 Session 缓存中。
OWIN 中间件在启动时实例化如下:
Public Sub ConfigureAuth(ByVal app As IAppBuilder)
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb ConfigureAuth() - STARTED" & vbLf)
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions())
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = appId,
.Scope = $"openid email profile offline_access {graphScopes}",
.Authority = sAuthority,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.TokenValidationParameters = New TokenValidationParameters With {
.ValidateIssuer = False
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = AddressOf OnAuthenticationFailedAsync,
.AuthorizationCodeReceived = AddressOf OnAuthorizationCodeReceivedAsync
}
})
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb ConfigureAuth() - COMPLETED" & vbLf)
End Sub
请注意,OWIN 配置了一对通知,一个指示成功获取授权码 (AuthorizationCodeReceived),另一个指示身份验证失败 (AuthenticationFailed)。每个都映射到一个对应的异步任务对象。任务定义如下:
Private Shared Function OnAuthenticationFailedAsync(ByVal notification As AuthenticationFailedNotification(Of OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb OnAuthenticationFailedAsync()" & vbLf)
notification.HandleResponse()
Dim redirect As String = $"~/Views/ErrorPage?message={notification.Exception.Message}"
If notification.ProtocolMessage IsNot Nothing AndAlso Not String.IsNullOrEmpty(notification.ProtocolMessage.ErrorDescription) Then
redirect += $"&debug={notification.ProtocolMessage.ErrorDescription}"
End If
notification.Response.Redirect(redirect)
Return Task.FromResult(0)
End Function
Private Async Function OnAuthorizationCodeReceivedAsync(ByVal notification As AuthorizationCodeReceivedNotification) As Task
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb OnAuthorizationCodeReceivedAsync()" & vbLf)
Dim signedInUser = New ClaimsPrincipal(notification.AuthenticationTicket.Identity)
Dim idClient As IConfidentialClientApplication = ConfidentialClientApplicationBuilder.Create(appId).WithRedirectUri(redirectUri).WithClientSecret(appSecret).Build()
Dim tokenStore As SessionTokenStore = New SessionTokenStore(idClient.UserTokenCache, HttpContext.Current, signedInUser)
Try
Dim scopes As String() = graphScopes.Split(" "c)
Dim authResult = Await idClient.AcquireTokenByAuthorizationCode(scopes, notification.Code).ExecuteAsync()
Dim userDetails = Await Helpers.GraphHelper.GetUserDetailsAsync(authResult.AccessToken)
Dim cachedUser = New CachedUser() With {
.DisplayName = userDetails.DisplayName,
.Email = If(String.IsNullOrEmpty(userDetails.Mail), userDetails.UserPrincipalName, userDetails.Mail),
.Avatar = String.Empty,
.CompanyName = userDetails.CompanyName
}
tokenStore.SaveUserDetails(cachedUser)
Catch ex As MsalException
Dim message As String = "AcquireTokenByAuthorizationCodeAsync threw an exception"
notification.HandleResponse()
notification.Response.Redirect($"~/Views/ErrorPage?message={message}&debug={ex.Message}")
Catch ex As Microsoft.Graph.ServiceException
Dim message As String = "GetUserDetailsAsync threw an exception"
notification.HandleResponse()
notification.Response.Redirect($"~/Views/ErrorPage?message={message}&debug={ex.Message}")
End Try
End Function
用户登录如下:
Public Shared Sub SignIn()
System.Diagnostics.Debug.WriteLine("AccountController.vb SignIn()")
If Not HttpContext.Current.Request.IsAuthenticated Then
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(New AuthenticationProperties With {
.RedirectUri = "/"
}, OpenIdConnectAuthenticationDefaults.AuthenticationType)
End If
End Sub
我没有收到任何运行时错误消息。没有构建错误或警告。一旦 OWIN 完成登录过程,该应用程序就会挂起。
简而言之,我试图理解为什么程序流没有从GetOwinContext().Authentication.Challenge() 方法传递到OnAuthorizationCodeReceivedAsync() 任务。我已经从有效的 MVC 示例中验证了这是预期的行为。
编辑:
在跟踪应用程序的 MVC/C# 和 WebForms/VB.NET 版本后,对两者进行并排比较表明应用程序的 WebForms 版本在 UseOpenIdConnectAuthentication() 方法处挂起。相关的 OpenIdConnectAuthenticationNotifications 已扩展为包括所有六个可用选项。
来自 MVC/C# Startup.Auth.cs:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Scope = $"openid email profile offline_access {graphScopes}",
Authority = "https://login.microsoftonline.com/common/v2.0",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync,
RedirectToIdentityProvider = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** RedirectToIdentityProvider");
return Task.FromResult(0);
},
MessageReceived = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** MessageReceived");
return Task.FromResult(0);
},
SecurityTokenReceived = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** SecurityTokenReceived");
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** SecurityTokenValidated");
return Task.FromResult(0);
}
}
}
);
收到以下通知:
- RedirectToIdentityProvider
- 收到消息
- 收到SecurityToken
- SecurityTokenValidated
-- 触发 OnAuthorizationCodeReceivedAsync() 方法,并按预期检索和缓存访问令牌。
来自 WebForms/VB.NET Startup.Auth.vb:
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = appId,
.Scope = $"openid email profile offline_access {graphScopes}",
.Authority = sAuthority,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.TokenValidationParameters = New TokenValidationParameters With {
.ValidateIssuer = False
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = AddressOf OnAuthenticationFailedAsync,
.AuthorizationCodeReceived = AddressOf OnAuthorizationCodeReceivedAsync,
.RedirectToIdentityProvider = Function(context)
Debug.WriteLine("*** RedirectToIdentityProvider")
Return Task.FromResult(0)
End Function,
.MessageReceived = Function(context)
Debug.WriteLine("*** MessageReceived")
Return Task.FromResult(0)
End Function,
.SecurityTokenReceived = Function(context)
Debug.WriteLine("*** SecurityTokenReceived")
Return Task.FromResult(0)
End Function,
.SecurityTokenValidated = Function(context)
Debug.WriteLine("*** SecurityTokenValidated")
Return Task.FromResult(0)
End Function
}
})
收到以下通知: - RedirectToIdentityProvider
-- 应用程序在等待时挂起,没有其他事件被触发。
我试图理解为什么相同的 OpenID Connect 方法会导致此应用的 MVC 和 WebForms 版本之间的行为如此显着不同。
【问题讨论】:
标签: vb.net webforms microsoft-graph-api msal