如何清理IdentityServer4中Cookie的冗余声明以节省空间
我在使用IdentityServer4的id_token流程,目前仅存储约2个自定义声明,但Cookie已接近3623/4093字节的大小限制。显然IdentityServer4向Cookie中注入了大量声明,希望至少移除占用过多空间的nonce声明。
客户端应用配置
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); var openIdConnectConfig = builder.Configuration.GetSection("OpenIdConnect"); builder.Services.AddAuthentication(options => { options.DefaultScheme = "cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("cookies", options => { options.Cookie.Name = "MyCookie"; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromMinutes(5); }) .AddOpenIdConnect("oidc", options => { options.Authority = openIdConnectConfig["Authority"]; options.ClientId = openIdConnectConfig["ClientId"]; options.ClientSecret = openIdConnectConfig["ClientSecret"]; //options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("profile"); options.MapInboundClaims = false; options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = false; // what does this even do? // is this the correct flow? options.ResponseType = "id_token"; // these have no effect... options.ClaimActions.DeleteClaim("nonce"); options.ClaimActions.DeleteClaim("aud"); options.ClaimActions.DeleteClaim("azp"); options.ClaimActions.DeleteClaim("acr"); options.ClaimActions.DeleteClaim("amr"); options.ClaimActions.DeleteClaim("iss"); options.ClaimActions.DeleteClaim("iat"); options.ClaimActions.DeleteClaim("nbf"); options.ClaimActions.DeleteClaim("exp"); options.ClaimActions.DeleteClaim("at_hash"); options.ClaimActions.DeleteClaim("c_hash"); options.ClaimActions.DeleteClaim("auth_time"); options.ClaimActions.DeleteClaim("ipaddr"); options.ClaimActions.DeleteClaim("platf"); options.ClaimActions.DeleteClaim("ver"); options.ClaimActions.DeleteClaim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"); options.ClaimActions.DeleteClaim("AspNet.Identity.SecurityStamp"); options.ClaimActions.DeleteClaim("role"); options.ClaimActions.DeleteClaim("preferred_username"); options.ClaimActions.DeleteClaim("email_verified"); // I added this just so `User` has `Name` property. options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; options.UsePkce = true; options.CallbackPath = "/signin-oidc"; options.AccessDeniedPath = "/AccessDenied"; // I got this from somewhere when IdentityServer4 was giving some error about "challenge code", not sure if it's doing anything right now. options.Events.OnRedirectToIdentityProvider = context => { // only modify requests to the authorization endpoint if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication) { // generate code_verifier var codeVerifier = CryptoRandom.CreateUniqueId(32); // store codeVerifier for later use context.Properties.Items.Add("code_verifier", codeVerifier); // create code_challenge string codeChallenge; using (var sha256 = SHA256.Create()) { var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier)); codeChallenge = Base64Url.Encode(challengeBytes); } // add code_challenge and code_challenge_method to request context.ProtocolMessage.Parameters.Add("code_challenge", codeChallenge); context.ProtocolMessage.Parameters.Add("code_challenge_method", "S256"); } return Task.CompletedTask; }; });
认证服务器Startup.cs配置
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); var builder = services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html options.EmitStaticAudienceClaim = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddAspNetIdentity<ApplicationUser>() .AddProfileService<MyProfileService>();
认证服务器客户端配置
new Client { ClientId = "kiosk_admin", ClientName = "Kiosk Admin", ClientSecrets = { new Secret("8696ba8c-b6ba-438a-9211-ee62ceea0144".Sha256()) }, AllowPlainTextPkce = true, RequirePkce = true, // `id_token` flow matches `implicit` gramt type AllowedGrantTypes = GrantTypes.Implicit, // where to redirect to after login RedirectUris = { "https://localhost:7177/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:7177/signout-callback-oidc" }, FrontChannelLogoutUri = "https://localhost:7177/signout-oidc", AllowOfflineAccess = true, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } }
自定义ProfileService实现
public class MyProfileService : IProfileService { protected UserManager<ApplicationUser> _userManager; protected ApplicationDbContext _context; public MyProfileService(UserManager<ApplicationUser> userManager, ApplicationDbContext context) { _userManager = userManager; _context = context; } /// <summary> /// user to manually inject desired claims /// </summary> public async Task GetProfileDataAsync(ProfileDataRequestContext context) { //>Processing var user = await _userManager.GetUserAsync(context.Subject); if (user != null) { var claims = _context.UserClaims .Where(x => x.UserId == user.Id) .Select(x => new Claim(x.ClaimType, x.ClaimValue)) .ToList(); claims.Add(new Claim("Name", user.UserName)); context.IssuedClaims.AddRange(claims.AsEnumerable()); context.IssuedClaims.RemoveAll(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" // we already have an email claim || c.Type == "preferred_username" // we already have a name claim || c.Type == "amr" // Authentication method reference. It tells you how the user authenticated (e.g., pwd means password). Useful for some scenarios. || c.Type == "idp" // Identity provider. Indicates how the user authenticated (e.g., local means they authenticated directly with your server). Useful for some scenarios. ); } } public async Task IsActiveAsync(IsActiveContext context) { //>Processing var user = await _userManager.GetUserAsync(context.Subject); context.IsActive = (user != null) && user.IsActive; } }
我需要一个简单的SSO解决方案,让多个应用共享同一Cookie实现用户认证,同时在各项目中使用基于声明的授权(每个用户角色对应一个独立声明),因此需要为Cookie腾出空间。除了清理冗余声明,若我的实现方式存在错误(比如当前认证流程是否适合我的场景),也请指出。
内容的提问来源于stack exchange,提问作者emrebener
相关产品推荐
相关产品推荐

