Azure 应用服务上的瞬时无限登录循环 - OpenIDConnect Auth with Azure AD
Transient Infinite Login Loop on Azure App Service - OpenIDConnect Auth with Azure AD
背景
所以我们有一个应用程序服务,它使用 OpenIdConnect 从另一个租户中的 Azure AD 进行身份验证。
登录适用于 IIS 的开发实例,它适用于我们的测试应用服务。我们在测试中看到了这个问题,并且在项目的整个测试阶段它都消失了 return。
现在我们已经部署到生产环境,但我们又遇到了这个问题。
问题
我们看到的是一段时间内一切正常,然后几个小时后,问题又会出现。
我们有一个解决方案来恢复服务——即在 Azure 控制面板中启用然后禁用应用程序服务身份验证。反之亦然 - 禁用然后启用将恢复服务。
代码
public void ConfigureAuth(IAppBuilder app)
{
//Azure AD Configuration
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//sets client ID, authority, and RedirectUri as obtained from web config
ClientId = clientId,
ClientSecret = appKey,
Authority = authority,
RedirectUri = redirectUrl,
CallbackPath = new PathString("/"), //use this line for production and test
//page that users are redirected to on logout
PostLogoutRedirectUri = redirectUrl,
//scope - the claims that the app will make
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
//setup multi-tennant support here, or set ValidateIssuer = true to config for single tennancy
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
//SaveSigninToken = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
}
}
);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential cred = new ClientCredential(clientId, appKey);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
//this token cache is stateful, we're going to populate it here, but we'll store it elsewhere in-case the user ends up accessing a different instance
AuthenticationContext authContext = new AuthenticationContext(authority, new NaiveSessionCache(userObjectId));
// If you create the redirectUri this way, it will contain a trailing slash.
// Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, cred, "https://graph.windows.net");
//populate the persistent token cache
testdb2Entities5 db = new testdb2Entities5();
PersistentTokenCache tc = await db.PersistentTokenCaches.FindAsync(userObjectId);
//if null, populate a new item
if (tc == null)
{
tc = new PersistentTokenCache();
tc.object_id = userObjectId;
tc.token = code;
db.PersistentTokenCaches.Add(tc);
await db.SaveChangesAsync();
}
else
{
tc.token = code;
await db.SaveChangesAsync();
}
}
//authentication failed notifications
private Task OnAuthenticationFailed(AuthenticationFailedNotification<Microsoft.IdentityModel.Protocols
.OpenIdConnect.OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
问题
因此,根据启用和禁用应用程序服务身份验证所做的任何操作,它显然是暂时修复问题。所以我认为这是一个与 cookie 相关的问题——因为这是在会话之间传输状态的唯一方法。这到底是什么问题?我需要采取哪些步骤来诊断和解决问题?
到目前为止,这似乎是 Katana 中一个已知错误的问题,在该错误中,Katana cookie 管理器和 ASP .NET cookie 管理器发生冲突并覆盖了彼此的 cookie。
以下是您可以参考的一些疑难解答:
1.Setting
app.UseCookieAuthentication(new CookieAuthenticationOptions {CookieSecure == CookieSecureOption.Always})
。这意味着 cookie 可能会随着您的身份验证而泄露。
2.Add SystemWebCookieManager
in UseCookieAuthentication
位于 Microsoft.Owin.Host.SystemWeb
Nuget 包中。请参考这个thread.
3.Split cookie。有人注意到如果 Cookie 字符超过浏览器限制(> 4096)就会出现问题。因此,为了克服这个问题,在每个大约 4000 个字符的 set-cookie 中,在需要时将所有 cookie 组合在一起以获得原始值。
有关如何将 Microsoft 登录添加到 ASP.NET 网络应用程序的更多详细信息,请参阅此 article。
更新:
通过安装 Kentor.OwinCookieSaver
nuget 包修复并在 app.UseCookieAuthentication(new CookieAuthenticationOptions());
之前添加 app.UseKentorOwinCookieSaver();
背景
所以我们有一个应用程序服务,它使用 OpenIdConnect 从另一个租户中的 Azure AD 进行身份验证。
登录适用于 IIS 的开发实例,它适用于我们的测试应用服务。我们在测试中看到了这个问题,并且在项目的整个测试阶段它都消失了 return。
现在我们已经部署到生产环境,但我们又遇到了这个问题。
问题
我们看到的是一段时间内一切正常,然后几个小时后,问题又会出现。
我们有一个解决方案来恢复服务——即在 Azure 控制面板中启用然后禁用应用程序服务身份验证。反之亦然 - 禁用然后启用将恢复服务。
代码
public void ConfigureAuth(IAppBuilder app)
{
//Azure AD Configuration
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//sets client ID, authority, and RedirectUri as obtained from web config
ClientId = clientId,
ClientSecret = appKey,
Authority = authority,
RedirectUri = redirectUrl,
CallbackPath = new PathString("/"), //use this line for production and test
//page that users are redirected to on logout
PostLogoutRedirectUri = redirectUrl,
//scope - the claims that the app will make
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
//setup multi-tennant support here, or set ValidateIssuer = true to config for single tennancy
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
//SaveSigninToken = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
}
}
);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential cred = new ClientCredential(clientId, appKey);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
//this token cache is stateful, we're going to populate it here, but we'll store it elsewhere in-case the user ends up accessing a different instance
AuthenticationContext authContext = new AuthenticationContext(authority, new NaiveSessionCache(userObjectId));
// If you create the redirectUri this way, it will contain a trailing slash.
// Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, cred, "https://graph.windows.net");
//populate the persistent token cache
testdb2Entities5 db = new testdb2Entities5();
PersistentTokenCache tc = await db.PersistentTokenCaches.FindAsync(userObjectId);
//if null, populate a new item
if (tc == null)
{
tc = new PersistentTokenCache();
tc.object_id = userObjectId;
tc.token = code;
db.PersistentTokenCaches.Add(tc);
await db.SaveChangesAsync();
}
else
{
tc.token = code;
await db.SaveChangesAsync();
}
}
//authentication failed notifications
private Task OnAuthenticationFailed(AuthenticationFailedNotification<Microsoft.IdentityModel.Protocols
.OpenIdConnect.OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
问题
因此,根据启用和禁用应用程序服务身份验证所做的任何操作,它显然是暂时修复问题。所以我认为这是一个与 cookie 相关的问题——因为这是在会话之间传输状态的唯一方法。这到底是什么问题?我需要采取哪些步骤来诊断和解决问题?
到目前为止,这似乎是 Katana 中一个已知错误的问题,在该错误中,Katana cookie 管理器和 ASP .NET cookie 管理器发生冲突并覆盖了彼此的 cookie。
以下是您可以参考的一些疑难解答:
1.Setting
app.UseCookieAuthentication(new CookieAuthenticationOptions {CookieSecure == CookieSecureOption.Always})
。这意味着 cookie 可能会随着您的身份验证而泄露。
2.Add SystemWebCookieManager
in UseCookieAuthentication
位于 Microsoft.Owin.Host.SystemWeb
Nuget 包中。请参考这个thread.
3.Split cookie。有人注意到如果 Cookie 字符超过浏览器限制(> 4096)就会出现问题。因此,为了克服这个问题,在每个大约 4000 个字符的 set-cookie 中,在需要时将所有 cookie 组合在一起以获得原始值。
有关如何将 Microsoft 登录添加到 ASP.NET 网络应用程序的更多详细信息,请参阅此 article。
更新:
通过安装 Kentor.OwinCookieSaver
nuget 包修复并在 app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseKentorOwinCookieSaver();