I'm using Asp.Net Core 3.1 with Identity. And here is my all configuration in Startup class. I'm trying to force the logged in user to logout if their accounts expired while using the app. I should configure the cookie correctly but i'm stuck on how to do this while having AddIdentity.
Here is my Startup
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>(options => {
options.SignIn.RequireConfirmedAccount = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityCore<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>()
//.AddDefaultTokenProviders()
.AddDefaultUI();
services.AddSingleton<IEmailSender, EmailSender>();
services.Configure<EmailOptions>(Configuration);
services.AddHangfire(config => config.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
services.AddHangfireServer();
services.AddControllersWithViews(); //?
services.AddRazorPages().AddRazorRuntimeCompilation(); //?
services.AddScoped<IExpirationJob, ExpirationJob>();
services.AddScoped<IReminderJob, EmailReminder>();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.Configure<IdentityOptions>(options =>
{
// Password settings.
//options.Password.RequireDigit = true;
//options.Password.RequireLowercase = true;
//options.Password.RequireNonAlphanumeric = true;
//options.Password.RequireUppercase = true;
//options.Password.RequiredLength = 6;
//options.Password.RequiredUniqueChars = 1;
// Lockout settings.
//options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
//options.Lockout.MaxFailedAccessAttempts = 5;
//options.Lockout.AllowedForNewUsers = true;
// User settings.
//options.User.AllowedUserNameCharacters =
// "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+ ";
//options.User.RequireUniqueEmail = false;
});
services.ConfigureApplicationCookie(config =>
{
config.Cookie.Name = "my.Cookie";
config.LoginPath = "/Home/Login";
config.AccessDeniedPath = "/Identity/Account/AccessDenied";
config.Cookie.HttpOnly = true;
config.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,
IWebHostEnvironment env,
IRecurringJobManager recurringJobManager,
IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseHangfireDashboard();
//app.UseHangfireDashboard("/hangfire", new DashboardOptions()
//{
// Authorization = new[] { new CustomAuthorizeFilter() }
//});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAuthentication();
app.UseAuthorization();
recurringJobManager.AddOrUpdate(
"End Users Subscription",
() => serviceProvider.GetService<IExpirationJob>().SetExpired(),
Cron.Minutely
);
recurringJobManager.AddOrUpdate(
"Send End of Subscription Reminder",
() => serviceProvider.GetService<IReminderJob>().SendReminder(),
Cron.Daily
);
app.Use(async (context, next) =>
{
_ = ExpirationJob.SetExpired(context);//pass the HttpContext to SetExpired
await next();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
Here is my ValidateAsync class
public class ValidateAsync
{
public static async Task ValidatingAsync(CookieValidatePrincipalContext context)
{
context = context ?? throw new ArgumentNullException(nameof(context));
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
if (claimsIdentity?.Claims == null || !claimsIdentity.Claims.Any())
{
await RejectPrincipal();
return;
}
UserManager<IdentityUser> userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
var user = await userManager.FindByNameAsync(context.Principal.FindFirstValue(ClaimTypes.NameIdentifier));
if (user == null || user.SecurityStamp != context.Principal.FindFirst(new ClaimsIdentityOptions().SecurityStampClaimType)?.Value)
{
await RejectPrincipal();
return;
}
async Task RejectPrincipal()
{
context.RejectPrincipal();
await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
Here is my SetExpired method
public interface IExpirationJob
{
Task SetExpired();
}
public class ExpirationJob : IExpirationJob
{
private readonly ApplicationDbContext _db;
private readonly IEmailSender _emailSender;
private readonly HttpContext _context;
public ExpirationJob(ApplicationDbContext db, IEmailSender emailSender, HttpContext context)
{
_db = db;
_emailSender = emailSender;
_context = context;
}
public async Task SetExpired()
{
foreach(var item in _db.Institution)
{
if (item.SubsEndDate != null)
{
if (item.SubsEndDate <= DateTime.Now)
{
item.Status = SD.StatusExpired;
Guid securityStamp = Guid.NewGuid();
item.Admin.SecurityStamp = securityStamp.ToString();
_context.Response.Cookies.Append("my.Cookie", "expired");
}
}
}
await _db.SaveChangesAsync();
}
}
After understanding the business logic, you can get the HttpContext
in the middleware, and modify the cookie value to force logout. Here is an example.
First, configure basic cookies in ConfigureServices
.
services.ConfigureApplicationCookie(config =>
{
config.Cookie.Name = "my.Cookie";
config.LoginPath = "/Home/Login";
config.AccessDeniedPath = "/Identity/Account/AccessDenied";
config.Cookie.HttpOnly = true;
config.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
});
Second, configure the middleware.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
IExpirationJob expirationJob)
{
//...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.Use(async(context,next)=>
{
expirationJob.SetExpired(context);//pass the HttpContext to SetExpired
await next();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Three, determine whether to overwrite the cookie by the expiration time SubscriptionEndDate
of the database.
public async Task SetExpired(HttpContext httpContext)
{
foreach (var item in _db.Institution)
{
if (item.SubscriptionEndDate != null)
{
if (item.SubscriptionEndDate == DateTime.Today)
{
item.Status = SD.StatusExpired;
Guid securityStamp = Guid.NewGuid();
item.SecurityStamp = securityStamp;
httpContext.Response.Cookies.Append("my.Cookie", "expired");
}
}
}
await _db.SaveChangesAsync();
}
Then, the cookie is invalid and this user will not access the resource.
Update:
An another method, if you want to pass the HttpContext
to SetExpired
as a parameter.
public static class MyClass { public static HttpContext http { get; set; } }
Then, you can assign values to http
in Statup.cs .
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IExpirationJob expirationJob) { //... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.Use(async(context,next)=> { MyClass.http = context; expirationJob.SetExpired(); await next(); }); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
Get it with this.
public async Task SetExpired() { var path=MyClass.http.Request.Path; //foreach (var item in _db.Institution) //{ // if (item.SubsEndDate != null) // { //... await _db.SaveChangesAsync(); }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.