使用 OpenIDConnect 时如何注销 ClaimsIdentity 用户
Posted
技术标签:
【中文标题】使用 OpenIDConnect 时如何注销 ClaimsIdentity 用户【英文标题】:How to logout ClaimsIdentity User when using OpenIDConnect 【发布时间】:2020-11-12 09:01:12 【问题描述】:我有使用 OpenIDConnect 身份验证的 ASP.NET Core 应用程序。在OnTokenValidated
事件中,我检查Authenticated
用户是否存在于我的应用程序的数据库中,如果不存在,那么我将抛出UnauthorizedAccessException
请注意,在OnTokenValidated
事件中,如果用户存在于应用程序的数据库中,我将创建新身份,否则我将保留经过身份验证的用户。
public class Startup
private readonly ILogger<Startup> _logger;
public IConfiguration Configuration get;
private IHostingEnvironment _environment get;
public Startup(IConfiguration configuration, IHostingEnvironment env, ILogger<Startup> logger)
Configuration = configuration;
_environment = env;
_logger = logger;
public void ConfigureServices(IServiceCollection services)
services.AddAuthentication(options =>
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
options.LoginPath = "/Home";
options.AccessDeniedPath = "/Account/Forbidden";
options.Cookie = new CookieBuilder()
Name = "myCookie",
HttpOnly = true,
;
options.SlidingExpiration = true;
)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
//options.ForwardChallenge
options.Authority = configuration["IdentityOptions:Authority"];
options.ClientId = configuration["IdentityOptions:ClientID"];
options.ResponseType = "id_token";
options.CallbackPath = "/Home";
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; //tells cookies scheme to persist user's identity in cookie.
options.SignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;//tells cookies scheme to remove persisted cookie
options.Scope.Add(OpenIdConnectScope.Email);
options.Events = new OpenIdConnectEvents()
OnTokenValidated = async context =>
var emailClaim = context.Principal.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Email);
// check if user exists in client applications db
CompanyUser cu = null;
using (var serviceProvider = services.BuildServiceProvider())
using (var serviceScope = serviceProvider.CreateScope())
using (var accountService = serviceScope.ServiceProvider.GetService<IAccountService>())
cu = await accountService.Authorize(emailClaim.Value);
if (cu == null)
// context.Principal.Identity.IsAuthenticated is true here
throw new UnauthorizedAccessException(string.Format("Could not find user for login '0' ", emailClaim.Value));
//We will create new identity to store only required claims.
var newIdentity = new ClaimsIdentity(context.Principal.Identity.AuthenticationType);
// keep the id_token for logout
newIdentity.AddClaim(new Claim(IdentityClaimTypes.IdToken, context.ProtocolMessage.IdToken));
// add email claim
newIdentity.AddClaim(emailClaim);
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(3);
// overwrite existing authentication ticket
context.Principal = new ClaimsPrincipal(newIdentity);
,
OnRedirectToIdentityProviderForSignOut = async context =>
var idTokenHint = context.HttpContext?.User?.FindFirst("id_token");
if (idTokenHint != null)
context.ProtocolMessage.IdTokenHint = idTokenHint.Value;
await Task.FromResult(0);
,
OnRemoteFailure = async context =>
//WHY context.HttpContext.User.Identity.IsAuthenticated is false here??
if (context.Failure is UnauthorizedAccessException)
context.Response.Redirect("/Account/AccessDenied");
else
context.Response.Redirect("/Account/Error");
context.HandleResponse();
await Task.FromResult(0);
;
);
问题
1> 在OnRemoteFailure
中,我如何在重定向到 AccessDenied 视图之前自动注销/清除unauthorized
用户? AccessDenied
视图可供匿名用户访问。
我试过了
OnRemoteFailure = async context =>
if (context.Failure is UnauthorizedAccessException)
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(context.HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(context.HttpContext, OpenIdConnectDefaults.AuthenticationScheme);
context.Response.Redirect("/Account/AccessDenied");
else
context.Response.Redirect("/Account/Error");
context.HandleResponse();
await Task.FromResult(0);
但是在OnTokenValidated
事件中context.HttpContext.User.Identity.IsAuthenticated
是假的,所以它不起作用。
【问题讨论】:
【参考方案1】:一个常见的错误是在调用 Signout 方法后执行 Reponse.Redirect,这听起来很明显。但不幸的是,这不起作用。 Signout 方法会创建自己的重定向响应,当您进行重定向时,您会“覆盖”这些重定向
实现这一点的正确方法是不返回任何内容,让 Signout 方法处理重定向:
public async Task DoLogout()
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
要重定向到备用网址,您可以尝试:
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties( )
RedirectUri = "alternativeUrl"
);
【讨论】:
我试过了,但这会将用户重定向到身份服务器的注销页面。我希望用户转到 AccessDenied 页面。我试过public async Task<IActionresult> AccessDenied() await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); return View("AccessDenied");
没用
它确实被重定向了。可能是我在这里问了错误的问题。我已经开始了一个新线程***.com/questions/63042878/…
澄清你的问题总是好的。但是,为什么不在这两个 Signout 方法上设置 AuthenticationProperties redirectUri 呢?或者尝试改变它们的顺序?
如果你不能让它工作,你总是可以作为一个黑客,只需清除本地会话 cookie 以在本地注销用户。
您对我的回答满意还是缺少什么?如果没有,请将我的回答标记为已接受。以上是关于使用 OpenIDConnect 时如何注销 ClaimsIdentity 用户的主要内容,如果未能解决你的问题,请参考以下文章
Microsoft OpenIdConnect Owin 响应
执行 Auth0/Google 联合 SSO 注销时如何将用户重定向回我的应用程序?
Azure AD B2C OpenID使用WS-Federation和SAML声明提供程序连接单一注销
post_logout_redirect_uri 为空 OpenIdConnect IdentityServer
在结束会话 URI 中使用 IP 地址时,OpenId Connect 用户未注销(发现文档 end_session_endpoint)。使用 Gluu 身份服务器