using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; namespace zero.Identity; public static class ZeroIdentityExtensions { // /// Adds the default identity system configuration for the specified User and Role types. /// /// The type representing a User in the system. /// The type representing a Role in the system. /// The services available in the application. /// An for creating and configuring the identity system. public static IdentityBuilder AddZeroIdentity(this IServiceCollection services) where TUser : ZeroIdentityUser, new() where TRole : ZeroIdentityRole, new() => AddZeroIdentity(services, null); /// /// Adds and configures the identity system for the specified User and Role types. /// /// The type representing a User in the system. /// The type representing a Role in the system. /// The services available in the application. /// An action to configure the . /// An for creating and configuring the identity system. public static IdentityBuilder AddZeroIdentity(this IServiceCollection services, Action setupAction) where TUser : ZeroIdentityUser, new() where TRole : ZeroIdentityRole, new() { // Services identity depends on services .AddOptions() .AddLogging(); // Services used by identity services.AddAuthentication(options => { options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; }) .AddCookie(IdentityConstants.ApplicationScheme, o => { o.LoginPath = new PathString("/account/login"); o.SlidingExpiration = true; o.ExpireTimeSpan = TimeSpan.FromDays(90); o.Cookie.Name = ZeroIdentityConstants.CookieNames.Application; o.Cookie.HttpOnly = true; o.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; o.Cookie.SameSite = SameSiteMode.Lax; o.Cookie.Path = "/"; o.Events = new CookieAuthenticationEvents { OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync }; }) .AddCookie(IdentityConstants.ExternalScheme, o => { o.Cookie.Name = ZeroIdentityConstants.CookieNames.External; o.ExpireTimeSpan = TimeSpan.FromMinutes(5); }) .AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o => { o.Cookie.Name = ZeroIdentityConstants.CookieNames.TwoFactorRememberMe; o.Events = new CookieAuthenticationEvents { OnValidatePrincipal = SecurityStampValidator.ValidateAsync }; }) .AddCookie(IdentityConstants.TwoFactorUserIdScheme, o => { o.Cookie.Name = ZeroIdentityConstants.CookieNames.TwoFactorUserId; o.ExpireTimeSpan = TimeSpan.FromMinutes(5); }); // Hosting doesn't add IHttpContextAccessor by default services.AddHttpContextAccessor(); // Identity services services.TryAddScoped, UserValidator>(); services.TryAddScoped, PasswordValidator>(); services.TryAddScoped, PasswordHasher>(); services.TryAddScoped(); services.TryAddScoped, RoleValidator>(); // No interface for the error describer so we can add errors without rev'ing the interface services.TryAddScoped(); services.TryAddScoped>(); services.TryAddScoped>(); services.TryAddScoped, UserClaimsPrincipalFactory>(); services.TryAddScoped, DefaultUserConfirmation>(); services.TryAddScoped>(); services.TryAddScoped>(); services.TryAddScoped>(); services.Configure(opts => { opts.ClaimsIdentity.UserIdClaimType = ZeroIdentityConstants.Claims.UserId; opts.ClaimsIdentity.UserNameClaimType = ZeroIdentityConstants.Claims.Username; opts.ClaimsIdentity.RoleClaimType = ZeroIdentityConstants.Claims.Role; opts.ClaimsIdentity.SecurityStampClaimType = ZeroIdentityConstants.Claims.SecurityStamp; opts.ClaimsIdentity.EmailClaimType = ZeroIdentityConstants.Claims.Email; opts.Password.RequireDigit = false; opts.Password.RequireLowercase = false; opts.Password.RequireUppercase = false; opts.Password.RequireNonAlphanumeric = false; opts.Password.RequiredLength = 8; opts.Password.RequiredUniqueChars = 1; opts.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(60); opts.Lockout.MaxFailedAccessAttempts = 5; opts.Lockout.AllowedForNewUsers = true; opts.User.RequireUniqueEmail = true; }); services.Configure(opts => { opts.ValidationInterval = TimeSpan.FromMinutes(90); }); if (setupAction != null) { services.Configure(setupAction); } IdentityBuilder builder = new(typeof(TUser), typeof(TRole), services); builder.AddDefaultTokenProviders(); builder.AddZeroIdentityStores(); return builder; } /// /// Configures the application cookie. /// /// The services available in the application. /// An action to configure the . /// The services. public static IServiceCollection ConfigureZeroApplicationCookie(this IServiceCollection services, Action configure) => services.Configure(IdentityConstants.ApplicationScheme, configure); /// /// Configure the external cookie. /// /// The services available in the application. /// An action to configure the . /// The services. public static IServiceCollection ConfigureZeroExternalCookie(this IServiceCollection services, Action configure) => services.Configure(IdentityConstants.ExternalScheme, configure); }