双重 OWIN 身份验证无法协同工作
Posted
技术标签:
【中文标题】双重 OWIN 身份验证无法协同工作【英文标题】:Double OWIN authentication not working together 【发布时间】:2018-06-08 09:54:33 【问题描述】:对于我的网站,我正在使用 OWIN OpenId 与第 3 方身份验证提供商进行集成,以允许访问者注册/登录/退出。 “第二”
我的应用程序也有一个测试环境,在将这些更改推送到生产环境之前,所有代码更改都会在该环境中进行测试。我使用 OWIN OpenId 以及“First”,通过另一个 3rd 方身份验证提供程序保护测试环境免受公共访问。只有经过身份验证的访问者才能访问测试环境网站。
现在的问题是它们都独立工作,但我似乎无法将它们结合起来。我想要实现的是,我可以通过使用 First 进行身份验证来访问测试环境,然后作为常规访问者,使用 Second 进行身份验证以查看为注册访问者设计的内容。
这就是我正在做的事情:
两个身份验证提供程序都使用 cookie 身份验证,但我给了它们一个不同的 AuthenticationType 以将它们分开。
if (IsEnabled("First"))
app.SetDefaultSignInAsAuthenticationType("First");
else
app.SetDefaultSignInAsAuthenticationType("Second");
// Configure First.
if (IsEnabled("First"))
app.UseCookieAuthentication(First.CookieAuthenticationOptions); // AuthenticationType is set to "First" in these options.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
ClientId = First.AADClientId,
Authority = First.Authority,
Notifications = new OpenIdConnectAuthenticationNotifications
AuthenticationFailed = context => ... ,
RedirectToIdentityProvider = context => ...
,
AuthenticationType = "First"
);
app.Map($"First.Path/login", config =>
config.Run(context =>
context.Authentication.Challenge(new AuthenticationProperties
RedirectUri = First.ReturnUrl, IsPersistent = true ,
"First"
);
context.Response.StatusCode = 401;
return context.Response.WriteAsync(string.Empty);
);
);
// Configure Second.
app.UseCookieAuthentication(Second.CookieAuthenticationOptions); // AuthenticationType is set to "Second" in these options.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
AuthenticationFailed = x => ...,
RedirectToIdentityProvider = x =>
var mgr = x.Options.ConfigurationManager as PolicyConfigurationManager;
if (x.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
var config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None,
x.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary["PolicyId"]);
x.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
else
var config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None,
x.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary["PolicyId"]);
x.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
var redirectUri = Second.ReturnPath;
x.ProtocolMessage.RedirectUri = redirectUri;
x.ProtocolMessage.PostLogoutRedirectUri = redirectUri;
,
SecurityTokenValidated = x => ...
,
Scope = "openid",
ResponseType = "id_token",
ReturnUri = Second.ReturnUri,
ClientId = Second.ClientId,
ConfigurationManager = GetConfigurationManager()
AuthenticationType = configuration.AuthenticationType
);
app.Map(Second.LoginPath, config =>
// Trigger unauthorized so that active authentication will redirect to active directory.
config.Run(context =>
// Set policy in context to mitigate null ref exception in Startup.Auth OnRedirectToIdentityProvider
context.Authentication.Challenge(
new AuthenticationProperties(new Dictionary<string, string>
"PolicyId", Second.LoginPolicyId
)
IsPersistent = true,
RedirectUri = returnUrl
, "Second");
context.Response.StatusCode = 401;
// Middleware will redirect us instead of using this output.
return context.Response.WriteAsync(string.Empty);
);
);
app.Map(Second.ReturnPath, config =>
config.Use((context, next) =>
// In case of login, we will never get here because we will get redirected by middleware.
context.Response.Redirect("/");
return Task.FromResult(0);
);
);
启用 First 后,我可以这样做
var identity = HttpContext.Current.GetOwinContext.Authentication.AuthenticateAsync("Second").Result?.Identity;
在后续请求中并具有 ClaimsIdentity。但是当 First 被启用时,由于某种原因,上面的 Result 为空。
我注意到,当我同时启用 First 和 Second,并将 DefaultSignInAsAuthenticationType 设置为“Second”时,First 不再起作用。如果我同时启用 First 和 Second,并使用以前的 First 身份验证 cookie 浏览网站,一切正常。
我猜想在某处设置身份验证 cookie 的返回方法需要一些 AuthenticationType 的引用,但我不知道在哪里做。
我错过了什么?
【问题讨论】:
【参考方案1】:诀窍是在配置 Second 时将 AuthenticationType 添加到 TokenValidationParameters 中,如下所示:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
AuthenticationFailed = x => ...,
RedirectToIdentityProvider = x =>
var mgr = x.Options.ConfigurationManager as PolicyConfigurationManager;
if (x.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
var config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None,
x.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary["PolicyId"]);
x.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
else
var config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None,
x.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary["PolicyId"]);
x.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
var redirectUri = Second.ReturnPath;
x.ProtocolMessage.RedirectUri = redirectUri;
x.ProtocolMessage.PostLogoutRedirectUri = redirectUri;
,
SecurityTokenValidated = x => ...
,
Scope = "openid",
ResponseType = "id_token",
ReturnUri = Second.ReturnUri,
ClientId = Second.ClientId,
ConfigurationManager = GetConfigurationManager(),
AuthenticationType = configuration.AuthenticationType,
// ADD THIS TO MAKE IT WORK:
TokenValidationParameters = new TokenValidationParameters
AuthenticationType = configuration.AuthenticationType
);
【讨论】:
以上是关于双重 OWIN 身份验证无法协同工作的主要内容,如果未能解决你的问题,请参考以下文章
IsPersistent 在 OWIN Cookie 身份验证中的工作原理
核心 5,PasswordSignInAsync 无法在双重身份验证方案中设置 cookie
实施 Identity 2.1 + OWIN OAuth JWT 不记名令牌时如何从 Web API 控制器端点进行身份验证
OWIN Cookie 身份验证 - 使用 Kerberos 委派模拟 SQL Server