2

I'm using ASP.Net MVC Core where I have the following code where it is a daily task that sets users as "Expired" if their subscription is over. But how can I also check and force them to logout if they are currently logged in?

   public interface IExpirationJob
{
    Task SetExpired();
}

public class ExpirationJob : IExpirationJob
{
    private readonly ApplicationDbContext _db;
    private readonly IEmailSender _emailSender;

    public ExpirationJob(ApplicationDbContext db, IEmailSender emailSender)
    {
        _db = db;
        _emailSender = emailSender;
    }

    public async Task SetExpired()
    {
        foreach(var item in _db.Institution)
        {
            if (item.SubscriptionEndDate != null)
            {
                if (item.SubscriptionEndDate == DateTime.Today) 
                {
                    item.Status = SD.StatusExpired;

                  //Here I want to check if the user is logged in, then force logout should be done. 

                }
            }
        }
        await _db.SaveChangesAsync();
    }
}

Any help is highly appreciated.

1 Answers1

1

You can add a SecurityStamp property of type GUID to users model and set SecurityStamp to cookie or jwt token.

then when a user login, you must change the SecurityStamp value to a new value and save SecurityStamp to cookie and any time user send a request to application you must check SecurityStamp saved in cookie with SecurityStamp of users in database. and if these properties wasn't equal togeter you must reject user and set sign out user.

public static async Task ValidateAsync(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);
    }
}

In startup class pass ValidateAsync method to OnValidatePrincipal and set ValidationInterval to zero.

services.ConfigureApplicationCookie(options =>
{
    //
    options.Events = new CookieAuthenticationEvents
    {
        OnValidatePrincipal = ValidateAsync
    };
}).Configure<SecurityStampValidatorOptions>(options =>
{
    options.ValidationInterval = TimeSpan.Zero;
});

finally in your method just update SecurityStamp value like this:

    public async Task SetExpired()
    {
        foreach(var item in _db.Institution)
        {
            if (item.SubscriptionEndDate != null)
            {
                if (item.SubscriptionEndDate == DateTime.Today) 
                {
                    item.Status = SD.StatusExpired;

                  //Here I want to check if the user is logged in, then force logout should be done.     
                  Guid securityStamp = Guid.NewGuid();
                  item.SecurityStamp = securityStamp;

                }
            }
        }
        await _db.SaveChangesAsync();
    }
Farhad Zamani
  • 5,381
  • 2
  • 16
  • 41
  • Where should I put the 'ValidateAsync'? – shahad alshuhail Nov 14 '20 at 17:10
  • Create `ValidateAsync` method inside of `ConfigureService` method in startup class and pass `ValidateAsync` to `OnValidatePrincipal ` in `options.Events` cookie – Farhad Zamani Nov 15 '20 at 18:36
  • Still not getting this :(.. I mean should I separate the ValidateAsync(CookieValidatePrincipalContext context) in different class under "Services" folder for example? .. You said inside configureServices but how is this ? – shahad alshuhail Nov 18 '20 at 19:50