ASP.NET Core 2.2 - User's Last Login Date


Ken Haggerty
Created 04/14/2019 - Updated 04/14/2019 19:24

This article will cover adding a create and last login date to a user with ASP.NET Core Identity. I will assume you have created a new ASP.NET Core 2.2 Razor Pages project with Individual User Accounts, updated the database with the CreateIdentitySchema migration, scaffolded the Identity UI and created an ApplicationUser extending the IdentityUser, see the Add UnconfirmedEmail Property to IdentityUser section in Require A Confirmed Email. Or you can download the free ASP.NET Core 2.2 - Bootstrap Native Project from Manage > Assets. I was able to restore, build and run the project with VS 2017, VS 2019 and VS Code.

Manage Assets

There are plenty of discussions about DateTime vs DateTimeOffset. DateTimeOffset is a data type in C# and SQL (since MS SQL 2008). DateTimeOffset can include time zone information but I always use UTC for continuity. This article will demonstrate adding a required created date and a nullable last login date for a user.

Let's start by adding the DateTimeOffset properties to the ApplicationUser.

Edit Entities > ApplicationUser.cs, add properties:
public class ApplicationUser : IdentityUser
{
    [PersonalData]
    public string UnconfirmedEmail { get; set; }

    [PersonalData]
    public DateTimeOffset CreateDate { get; set; }

    [PersonalData]
    public DateTimeOffset LastLoginDate { get; set; }
}
Run the command "Add-Migration CreateAndLastLoginDates" from the Package Manager Console in VS 2017.
Edit the CreateAndLastLoginDates Migration class, declare LastLoginDate as nullable:
public partial class CreateAndLastLoginDates : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn(
            name: "CreateDate",
            table: "AspNetUsers",
            nullable: false,
            defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));

        migrationBuilder.AddColumn(
            name: "LastLoginDate",
            table: "AspNetUsers",
            nullable: true);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "CreateDate",
            table: "AspNetUsers");

        migrationBuilder.DropColumn(
            name: "LastLoginDate",
            table: "AspNetUsers");
    }
}
Run the command "Update-Database".
Edit Register.cshtml.cs > OnPostAsync, add CreateDate to new user:
var user = new ApplicationUser { UserName = Input.UserName, Email = Input.Email, CreateDate = DateTimeOffset.UtcNow };
Edit Login.cshtml.cs > OnPostAsync > ModelState.IsValid > result.Succeeded, update LastLoginDate for user:
var user = await _userManager.FindByNameAsync(Input.UserName);
if (user == null)
{
    return NotFound("Unable to load user for update last login.");
}
user.LastLoginDate = DateTimeOffset.UtcNow;
var lastLoginResult = await _userManager.UpdateAsync(user);
if (!lastLoginResult.Succeeded)
{
    throw new InvalidOperationException($"Unexpected error occurred setting the last login date" +
        $" ({lastLoginResult.ToString()}) for user with ID '{user.Id}'.");
}
Edit ExternalLogin.cshtml.cs > OnGetCallbackAsync > ExternalLoginSignInAsync > result.Succeeded, update LastLoginDate for user:
var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
if (user == null)
{
    return NotFound("Unable to load user for update last login.");
}
user.LastLoginDate = DateTimeOffset.UtcNow;
var lastLoginResult = await _userManager.UpdateAsync(user);
if (!lastLoginResult.Succeeded)
{
    throw new InvalidOperationException($"Unexpected error occurred setting the last login date" +
        $" ({lastLoginResult.ToString()}) for user with ID '{user.Id}'.");
}

For LoginWith2fa and LoginWithRecoveryCode, you need to inject the ApplicationUserManager.

Edit LoginWith2fa.cshtml.cs > OnPostAsync > TwoFactorAuthenticatorSignInAsync > result.Succeeded, update LastLoginDate for user:
user.LastLoginDate = DateTimeOffset.UtcNow;
var lastLoginResult = await _userManager.UpdateAsync(user);
if (!lastLoginResult.Succeeded)
{
    throw new InvalidOperationException($"Unexpected error occurred setting the last login date" +
        $" ({lastLoginResult.ToString()}) for user with ID '{user.Id}'.");
}
Edit LoginWithRecoveryCode.cshtml.cs > OnPostAsync > TwoFactorRecoveryCodeSignInAsync > result.Succeeded, update LastLoginDate for user:
user.LastLoginDate = DateTimeOffset.UtcNow;
var lastLoginResult = await _userManager.UpdateAsync(user);
if (!lastLoginResult.Succeeded)
{
    throw new InvalidOperationException($"Unexpected error occurred setting the last login date" +
        $" ({lastLoginResult.ToString()}) for user with ID '{user.Id}'.");
}

Article Tags:

Claims Identity Model Scaffold

2 Following


Comment Count = 3

solarseven
solarseven
Member Since 06/18/2019
Posted 06/27/2019 20:37

Edit Login.cshtml.cs > OnPostAsync > ModelState.IsValid > result.Succeeded, update LastLoginDate for user: !--> Its not clear to me where to exactly add this, it says update, however this code is not in the file. So I guess it has to be added, as far I figured what its saying it has to go in the result.Succeeded if statement. Is this correct? <--!

Ken Haggerty
Ken Haggerty *
Member Since 01/04/2019
Posted 06/27/2019 20:55

Yes. As a registered member, you may download the Bootstrap Native Project from Manage > Assets which implements the features from most of the articles including User's Last Login Date. Then compare your code to the project code.

solarseven
solarseven
Member Since 06/18/2019
Posted 06/28/2019 12:40

Thanks for the support, checking the finished project is a great way to get the project working on my side too. Cheers Ken!

Please log in to comment or follow.

Login Register