ASP.NET Core 3.1 - Confirmed Email Address

Ken Haggerty
Created 02/20/2021 - Updated 02/23/2021 23:38

This article will describe the implementation of a confirmed email address for a user. I will assume you have downloaded the ASP.NET Core 3.1 - Users Without Identity Project or created a new ASP.NET Core 3.1 Razor Pages project. See Tutorial: Get started with Razor Pages in ASP.NET Core. You should review the earlier articles of the Users Without Identity Project series.

Users Without Identity Project and Article Series

UWIP v2 implements Email, EmailUppercase, EmailConfirmed, and UnconfirmedEmail properties for the AppUser and a new user registration page with an email address input.

Entities > AppUser:
[Required]
[StringLength(320)]
public string Email { get; set; }
[Required]
[StringLength(320)]
public string EmailUppercase { get; set; }
[Display(Name = "Confirmed")]
public bool EmailConfirmed { get; set; }
[Display(Name = "Unconfirmed Email")]
[Required(AllowEmptyStrings = true)]
[StringLength(320)]
public string UnconfirmedEmail { get; set; }

When a new user registers, an AppUser is created. The EmailConfirmed defaults to false, EmailUppercase is set with the Email.ToUpper() method, and UnconfirmedEmail is set to an empty string. UWIP v2 implements a unique index on EmailUppercase for user queries by email address.

Data > ApplicationDbContext > OnModelCreating:
builder.Entity<AppUser>().HasIndex(u => u.EmailUppercase).IsUnique();

An email is sent to the Email address with a link to a email confirmation page. The link includes an EmailConfirmation token based on the new AppUser's SecurityStamp and provided by the TokenService. The token is used to verify the user has access to the Email address. The email confirmation page sets the AppUser's EmailConfirmed to true if the token is verified.

Pages > Account > ConfirmEmail.cshtml.cs:
public async Task<IActionResult> OnGetAsync(int id, string code)
{
    if (id == 0 || string.IsNullOrEmpty(code)) return NotFound();

    var appUser = await _userService.GetAppUserByIdAsync(id).ConfigureAwait(false);
    if (appUser == null) return NotFound();

    if (await _tokenService.ValidateEmailConfirmationTokenAsync(appUser, code).ConfigureAwait(false))
    {
        if (!await _userService.ConfirmAppUserEmailAsync(id).ConfigureAwait(false))
            throw new InvalidOperationException($"An error occurred confirming an AppUser email.");
    }
    else
    {
        ModelState.AddModelError(string.Empty, "Invalid or expired code.");
        ShowInvalid = true;
    }                

    return Page();
}

When the token is invalid, the ConfirmEmail page displays further information using the ShowInvalid property.

Invalid Token
Invalid Token Mobile

The SignInService. PasswordSignInAsync function returns a NotAllowed Microsoft. AspNetCore. Identity. SignInResult if the user attempts to log in without a confirmed email.

Pages > Account > Login > OnPostAsync:
var result = await _signInService.PasswordSignInAsync(Input.LoginName, Input.Password, Input.RememberMe)
    .ConfigureAwait(false);
if (result.Succeeded)
{
    if (!await _userService.UpdateAppUserLastLoginDateAsync(appUserId).ConfigureAwait(false))
        throw new InvalidOperationException($"Unable to update AppUser LastLoginDate.");

    if (!Url.IsLocalUrl(returnUrl)) returnUrl = Url.Content("~/");

    return LocalRedirect(returnUrl);
}
else if (result.RequiresTwoFactor)
    return RedirectToPage("./LoginWith2fa", new { Input.RememberMe, ReturnUrl = returnUrl });
else if (result.IsNotAllowed)
{
    ModelState.AddModelError(string.Empty, "Email is not confirmed.");
    AppUserId = appUserId;
    ShowResend = true;
    return Page();
}
else if (result.IsLockedOut)
    return RedirectToPage("./LockedOut");
else
{
    if (!await _userService.UpdateAppUserAccessFailedCountAsync(appUserId).ConfigureAwait(false))
        throw new InvalidOperationException($"Unable to update AppUser AccessFailedCount.");
}

The ShowResend property displays an option to resend the confirmation email.

Resend Email
Resend Email Mobile

The UnconfirmedEmail page allows the user to update their email address before sending a new confirmation email.

Update Email
Update Email Mobile

UWIP v2 uses the UnconfirmedEmail property when the user updates their email address.

Manage Email
Manage Email Mobile
Update 02/23/2021

I updated the article links.

Comment Count = 0

Please log in to comment.

Login Register
Logged in users receive web notifications.
Web Notifications