从IdentityServer4的OpenIdConnect获取IsPersistent设置并同步客户端Cookie过期
Great question! Since your ASP.NET MVC app (non-.NET Core) and IdentityServer4 maintain separate cookies, you need to explicitly pass the "persistent login" state from IdentityServer to your client and apply it to your app's cookie. Here are two reliable approaches:
Approach 1: Pass Persistence State via Custom Claim (Most Direct)
First, you'll modify IdentityServer4 to include the "Remember Me" state as a custom claim in issued tokens, then read that claim in your MVC client to configure the app cookie.
Step 1: Add Custom Claim in IdentityServer4
In your IdentityServer4 implementation, update the login logic and profile service to include the persistence state:
- Update Login Action: In your AccountController's
Loginmethod, when signing in the user, add a custom claim for the "Remember Me" value:
public async Task<IActionResult> Login(LoginInputModel model) { // Validate user credentials... var isPersistent = model.RememberMe; var claims = new List<Claim> { new Claim("is_persistent", isPersistent.ToString()), // Add other required claims (name, email, etc.) }; var props = new AuthenticationProperties { IsPersistent = isPersistent }; await HttpContext.SignInAsync(model.Username, model.Username, props, claims); // Redirect to return URL... }
- Configure Profile Service: Ensure the custom claim is included in tokens issued to clients. If you have a custom
IProfileService, updateGetProfileDataAsync:
public class CustomProfileService : IProfileService { public async Task GetProfileDataAsync(ProfileDataRequestContext context) { // Add existing profile claims... // Include the is_persistent claim if present var isPersistentClaim = context.Subject.Claims.FirstOrDefault(c => c.Type == "is_persistent"); if (isPersistentClaim != null) { context.IssuedClaims.Add(isPersistentClaim); } await Task.CompletedTask; } // Implement other IProfileService methods... }
Step 2: Apply Persistence State in MVC Client
In your ASP.NET MVC app's OIDC configuration, use the SecurityTokenValidated notification to read the custom claim and set your app cookie's persistence:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { ClientId = "your-client-id", Authority = "https://your-identityserver-url", RedirectUri = "https://your-mvc-app-url/signin-oidc", PostLogoutRedirectUri = "https://your-mvc-app-url/signout-callback-oidc", ResponseType = "code id_token", Scope = "openid profile", UseTokenLifetime = false, // Important: Disable to control cookie lifetime independently Notifications = new OpenIdConnectAuthenticationNotifications { SecurityTokenValidated = async n => { // Read the custom is_persistent claim from the identity var isPersistentClaim = n.AuthenticationTicket.Identity.FindFirst("is_persistent"); bool isPersistent = false; if (isPersistentClaim != null) { bool.TryParse(isPersistentClaim.Value, out isPersistent); } // Set the app cookie's persistence and expiration var properties = n.AuthenticationTicket.Properties; properties.IsPersistent = isPersistent; if (isPersistent) { // Match your IdentityServer's persistent cookie expiration (e.g., 30 days) properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30); } else { // Session cookie: expires when browser closes properties.ExpiresUtc = null; } } } });
Approach 2: Use Authentication Ticket Properties (No Custom Claim)
If you prefer not to add a custom claim, you can rely on IdentityServer4's authentication ticket properties. This works if your IdentityServer4 configuration propagates the persistence state through the OIDC flow:
SecurityTokenValidated = async n => { // Check if the authentication ticket from IdentityServer has IsPersistent set bool isPersistent = n.AuthenticationTicket.Properties.IsPersistent; // Apply to your app's cookie n.AuthenticationTicket.Properties.IsPersistent = isPersistent; if (isPersistent) { n.AuthenticationTicket.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30); } else { n.AuthenticationTicket.Properties.ExpiresUtc = null; } }
Final Cookie Configuration
Make sure your app's cookie authentication is configured to respect the IsPersistent setting:
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), ExpireTimeSpan = TimeSpan.FromDays(30), // Used when IsPersistent is true SlidingExpiration = true, // Refresh expiration on activity });
This setup ensures your MVC app's cookie matches the persistence behavior of IdentityServer4's login cookie: when "Remember Me" is checked, the cookie persists for your configured duration; when unchecked, it's a session cookie that clears when the browser closes.
内容的提问来源于stack exchange,提问作者gilm0079

