问题
I'm, using RC1 bits and external (Google) authentication, no Identity.EntityFramework.
During login, I set 'Remember me' flag.
Logged-in user survives browser restart (I see cookie set to expire in 14 days) and website restart.
But after some time of inactivity (about 15 min), no matter browser/site were restarted or not, refreshing page lead to signing out, logs says:
info: Microsoft.AspNet.Authentication.Cookies.CookieAuthenticationMiddleware:
AuthenticationScheme: Microsoft.AspNet.Identity.Application signed out.
AuthenticationScheme: Microsoft.AspNet.Identity.External signed out.
AuthenticationScheme: Microsoft.AspNet.Identity.TwoFactorUserId signed out.
This looks like "sessions" in previous ASP, but I do not use any sessions here.
This is my local developer machine, no IIS, direct Kestrel connection to 5000 port, so this is not data-protection problem
Why user forced to sign out?
Update: my Startup.cs
file:
public void ConfigureServices(IServiceCollection services)
{
....
var identityBuilder = services
.AddIdentity<User, UserRole>(options =>
{
options.User.AllowedUserNameCharacters = null;
options.Cookies.ApplicationCookie.LoginPath = "/user/login";
options.Cookies.ApplicationCookie.LogoutPath = "/user/logout";
});
identityBuilder.Services
.AddScoped<IUserStore<User>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>())
.AddScoped<IRoleStore<UserRole>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>());
identityBuilder
.AddDefaultTokenProviders();
....
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
....
app.UseIdentity();
app.UseGoogleAuthentication(options =>
{
options.ClientId = Configuration["OAuth:Google:ClientId"];
options.ClientSecret = Configuration["OAuth:Google:Secret"];
});
....
SportCmsDb
is DbContext
and also implements IUserStore<User>
, IUserLoginStore<User>
, IUserEmailStore<User>
, IRoleStore<UserRole>
, IUserClaimStore<User>
Update 2
I enabled detailed (debug) logging and found that when user get signed out - prior to this my IUserStore<User>.FindByIdAsync
is called. With real/existing user id, and function returning correct non-null User. Everything seems good. But my loaded-from-db User
is "rejected" and forced to sign out. There is not additional log messages that can reveal why/where.
回答1:
Wow, I solved it!
TL;DR
I need to implement IUserSecurityStampStore<User>
on my custom UserManager
(aka SportCmsDb
).
Details
During AddIdentity
call (in Startup.cs
ConfigureServices
method) IdentityOptions are configured with default instance of IdentityCookieOptions. In constructor of IdentityCookieOptions
instance of ApplicationCookie
(of type CookieAuthenticationOptions
) is created with handler CookieAuthenticationEvents.OnValidatePrincipal
set to SecurityStampValidator.ValidatePrincipalAsync static method.
During UseIdentity
call (in Startup.cs
Configure
method) CookieAuthenticationMiddleware
is configured with IdentityOptions.Cookies.ApplicationCookie options.
CookieAuthenticationHandler
(created by CookieAuthenticationMiddleware
) in it's HandleAuthenticateAsync
method reads ticket from cookie and call Options.Events.ValidatePrincipal handler for validation.
Effectively, SecurityStampValidator.ValidatePrincipalAsync
is called. This method checks that enough time has elapsed since cookie was issued (30 min by default) and calls ISecurityStampValidator.validateAsync
(lines 81-82).
Default implementation of ISecurityStampValidator
is SecurityStampValidator<TUser>
. It calls SignInManager<TUser>.ValidateSecurityStampAsync and when null is returned - rejects principal and forces user to sign out (lines 30-40).
SignInManager<TUser>
in its ValidateSecurityStampAsync method tries to read security stamp from User
and returns null if it can't (if UserManager<User>
does not supports this interface) or stamp does not match saved one (in cookie).
My custom UserManager
does not implement IUserSecurityStampStore<User>
. Bingo.
回答2:
Thank you very much for the previous answers, I worked with this problem today and I resolve with this:
1.- Custom UserStore.cs:
public class UserStore : IUserStore<User>,
IUserPasswordStore<User>,
IUserEmailStore<User>,
IUserRoleStore<User>,
IUserSecurityStampStore<User>
{
//omitted...
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
if (user.SecurityStamp == null) {
return Task.FromResult("AspNet.Identity.SecurityStamp");
}
return Task.FromResult(user.SecurityStamp);
}
}
2.- In User.cs And DB Table User add SecurityStamp as string.
For TEST change default 30m to 1m in Startup.cs:
services.Configure<SecurityStampValidatorOptions>(options => {
options.ValidationInterval = TimeSpan.FromMinutes(1);
});
来源:https://stackoverflow.com/questions/37162791/asp-net-5-identity-3-users-get-signed-out-after-some-time