diff --git a/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml b/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml
index ddb1f9a2..9863af53 100644
--- a/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml
+++ b/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml
@@ -29,6 +29,9 @@
                         @Html.DisplayNameFor(m => m.Input.RememberMe)
                     
                 
+                
                 
                     
                 
@@ -82,5 +85,6 @@
 
 
 @section Scripts {
+    
     
 }
diff --git a/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml.cs b/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml.cs
index a3ea80a7..5bb5f29f 100644
--- a/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml.cs
+++ b/EssentialCSharp.Web/Areas/Identity/Pages/Account/Login.cshtml.cs
@@ -1,15 +1,19 @@
 using System.ComponentModel.DataAnnotations;
 using EssentialCSharp.Web.Areas.Identity.Data;
+using EssentialCSharp.Web.Models;
+using EssentialCSharp.Web.Services;
 using EssentialCSharp.Web.Services.Referrals;
 using Microsoft.AspNetCore.Authentication;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
 
 namespace EssentialCSharp.Web.Areas.Identity.Pages.Account;
 
-public class LoginModel(SignInManager signInManager, UserManager userManager, ILogger logger, IReferralService referralService) : PageModel
+public class LoginModel(SignInManager signInManager, UserManager userManager, ILogger logger, IReferralService referralService, ICaptchaService captchaService, IOptions optionsAccessor) : PageModel
 {
+    public CaptchaOptions CaptchaOptions { get; } = optionsAccessor.Value;
     private InputModel? _Input;
     [BindProperty]
     public InputModel Input
@@ -60,49 +64,119 @@ public async Task OnGetAsync(string? returnUrl = null)
     public async Task OnPostAsync(string? returnUrl = null)
     {
         returnUrl ??= Url.Content("~/");
+        string? hCaptcha_response = Request.Form[CaptchaOptions.HttpPostResponseKeyName];
 
         ExternalLogins = (await signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
 
-        if (ModelState.IsValid)
+        if (!ModelState.IsValid)
         {
-            Microsoft.AspNetCore.Identity.SignInResult result;
-            if (Input.Email is null)
-            {
-                return RedirectToPage(Url.Content("~/"), new { ReturnUrl = returnUrl });
-            }
-            EssentialCSharpWebUser? foundUser = await userManager.FindByEmailAsync(Input.Email);
-            if (Input.Password is null)
-            {
-                return RedirectToPage(Url.Content("~/"), new { ReturnUrl = returnUrl });
-            }
-            if (foundUser is not null)
-            {
-                result = await signInManager.PasswordSignInAsync(foundUser, Input.Password, Input.RememberMe, lockoutOnFailure: true);
-                // Call the referral service to get the referral ID and set it onto the user claim
-                _ = await referralService.EnsureReferralIdAsync(foundUser);
-            }
-            else
-            {
-                result = await signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
-            }
-            if (result.Succeeded)
-            {
-                logger.LogInformation("User logged in.");
-                return LocalRedirect(returnUrl);
-            }
-            if (result.RequiresTwoFactor)
-            {
-                return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
-            }
-            if (result.IsLockedOut)
+            return Page();
+        }
+
+        if (hCaptcha_response is null)
+        {
+            ModelState.AddModelError(CaptchaOptions.HttpPostResponseKeyName, HCaptchaErrorDetails.GetValue(HCaptchaErrorDetails.MissingInputResponse).FriendlyDescription);
+            return Page();
+        }
+
+        HCaptchaResult? response = await captchaService.VerifyAsync(hCaptcha_response);
+        if (response is null)
+        {
+            ModelState.AddModelError(CaptchaOptions.HttpPostResponseKeyName, "Error: HCaptcha API response unexpectedly null");
+            return Page();
+        }
+
+        if (response.Success)
+        {
+            if (ModelState.IsValid)
             {
-                logger.LogWarning("User account locked out.");
-                return RedirectToPage("./Lockout");
+                Microsoft.AspNetCore.Identity.SignInResult result;
+                if (Input.Email is null)
+                {
+                    return RedirectToPage(Url.Content("~/"), new { ReturnUrl = returnUrl });
+                }
+                EssentialCSharpWebUser? foundUser = await userManager.FindByEmailAsync(Input.Email);
+                if (Input.Password is null)
+                {
+                    return RedirectToPage(Url.Content("~/"), new { ReturnUrl = returnUrl });
+                }
+                if (foundUser is not null)
+                {
+                    result = await signInManager.PasswordSignInAsync(foundUser, Input.Password, Input.RememberMe, lockoutOnFailure: true);
+                    // Call the referral service to get the referral ID and set it onto the user claim
+                    _ = await referralService.EnsureReferralIdAsync(foundUser);
+                }
+                else
+                {
+                    result = await signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
+                }
+                if (result.Succeeded)
+                {
+                    logger.LogInformation("User logged in.");
+                    return LocalRedirect(returnUrl);
+                }
+                if (result.RequiresTwoFactor)
+                {
+                    return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
+                }
+                if (result.IsLockedOut)
+                {
+                    logger.LogWarning("User account locked out.");
+                    return RedirectToPage("./Lockout");
+                }
+                else
+                {
+                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
+                    return Page();
+                }
             }
-            else
+        }
+        else
+        {
+            switch (response.ErrorCodes?.Length)
             {
-                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
-                return Page();
+                case 0:
+                    throw new InvalidOperationException("The HCaptcha determined the passcode is not valid, and does not meet the security criteria");
+                case > 1:
+                    throw new InvalidOperationException("HCaptcha returned error codes: " + string.Join(", ", response.ErrorCodes));
+                default:
+                    {
+                        if (response.ErrorCodes is null)
+                        {
+                            throw new InvalidOperationException("HCaptcha returned error codes unexpectedly null");
+                        }
+                        if (HCaptchaErrorDetails.TryGetValue(response.ErrorCodes.Single(), out HCaptchaErrorDetails? details))
+                        {
+                            switch (details.ErrorCode)
+                            {
+                                case HCaptchaErrorDetails.MissingInputResponse:
+                                case HCaptchaErrorDetails.InvalidInputResponse:
+                                case HCaptchaErrorDetails.InvalidOrAlreadySeenResponse:
+                                    ModelState.AddModelError(string.Empty, details.FriendlyDescription);
+                                    logger.LogInformation("HCaptcha returned error code: {ErrorDetails}", details.ToString());
+                                    break;
+                                case HCaptchaErrorDetails.BadRequest:
+                                    ModelState.AddModelError(string.Empty, details.FriendlyDescription);
+                                    logger.LogInformation("HCaptcha returned error code: {ErrorDetails}", details.ToString());
+                                    break;
+                                case HCaptchaErrorDetails.MissingInputSecret:
+                                case HCaptchaErrorDetails.InvalidInputSecret:
+                                case HCaptchaErrorDetails.NotUsingDummyPasscode:
+                                case HCaptchaErrorDetails.SitekeySecretMismatch:
+                                    logger.LogCritical("HCaptcha returned error code: {ErrorDetails}", details.ToString());
+                                    break;
+                                default:
+                                    throw new InvalidOperationException("HCaptcha returned unknown error code: " + details?.ErrorCode);
+                            }
+                        }
+                        else
+                        {
+                            throw new InvalidOperationException("HCaptcha returned unknown error code: " + response.ErrorCodes.Single());
+                        }
+
+                        break;
+                    }
+
             }
         }
 
diff --git a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml
index d457b6b8..ce2c304f 100644
--- a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml
+++ b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml
@@ -16,11 +16,15 @@
                 
                 
             
+            
             
         
     
 
 
 @section Scripts {
+    
     
 }
diff --git a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs
index 8ef99283..15c4400e 100644
--- a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs
+++ b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs
@@ -2,18 +2,22 @@
 using System.Text;
 using System.Text.Encodings.Web;
 using EssentialCSharp.Web.Areas.Identity.Data;
+using EssentialCSharp.Web.Models;
+using EssentialCSharp.Web.Services;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.AspNetCore.Identity.UI.Services;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.RazorPages;
 using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Options;
 
 namespace EssentialCSharp.Web.Areas.Identity.Pages.Account;
 
 [AllowAnonymous]
-public class ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender) : PageModel
+public class ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender, ICaptchaService captchaService, IOptions optionsAccessor) : PageModel
 {
+    public CaptchaOptions CaptchaOptions { get; } = optionsAccessor.Value;
     private InputModel? _Input;
     [BindProperty]
     public InputModel Input
@@ -31,43 +35,108 @@ public class InputModel
 
     public async Task OnPostAsync()
     {
+        string? hCaptcha_response = Request.Form[CaptchaOptions.HttpPostResponseKeyName];
+
         if (!ModelState.IsValid)
         {
             return Page();
         }
 
-        if (Input.Email is null)
+        if (hCaptcha_response is null)
         {
-            ModelState.AddModelError(string.Empty, "Error: Email is null. Please enter in an email");
+            ModelState.AddModelError(CaptchaOptions.HttpPostResponseKeyName, HCaptchaErrorDetails.GetValue(HCaptchaErrorDetails.MissingInputResponse).FriendlyDescription);
             return Page();
         }
 
-        EssentialCSharpWebUser? user = await userManager.FindByEmailAsync(Input.Email);
-        if (user is null)
+        HCaptchaResult? response = await captchaService.VerifyAsync(hCaptcha_response);
+        if (response is null)
         {
-            return NotFound($"Unable to load user with ID '{userManager.GetUserId(User)}'.");
+            ModelState.AddModelError(CaptchaOptions.HttpPostResponseKeyName, "Error: HCaptcha API response unexpectedly null");
+            return Page();
         }
 
-        string userId = await userManager.GetUserIdAsync(user);
-        string code = await userManager.GenerateEmailConfirmationTokenAsync(user);
-        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
-        string? callbackUrl = Url.Page(
-            "/Account/ConfirmEmail",
-            pageHandler: null,
-            values: new { userId = userId, code = code },
-            protocol: Request.Scheme);
-
-        if (callbackUrl is null)
+        if (response.Success)
         {
-            ModelState.AddModelError(string.Empty, "Error: callback url unexpectedly null.");
+            if (Input.Email is null)
+            {
+                ModelState.AddModelError(string.Empty, "Error: Email is null. Please enter in an email");
+                return Page();
+            }
+
+            EssentialCSharpWebUser? user = await userManager.FindByEmailAsync(Input.Email);
+            if (user is null)
+            {
+                return NotFound($"Unable to load user with ID '{userManager.GetUserId(User)}'.");
+            }
+
+            string userId = await userManager.GetUserIdAsync(user);
+            string code = await userManager.GenerateEmailConfirmationTokenAsync(user);
+            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
+            string? callbackUrl = Url.Page(
+                "/Account/ConfirmEmail",
+                pageHandler: null,
+                values: new { userId = userId, code = code },
+                protocol: Request.Scheme);
+
+            if (callbackUrl is null)
+            {
+                ModelState.AddModelError(string.Empty, "Error: callback url unexpectedly null.");
+                return Page();
+            }
+            await emailSender.SendEmailAsync(
+                Input.Email,
+                "Confirm your email",
+                $"Please confirm your account by clicking here.");
+
+            ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email. If you can't find the email, please check your spam folder.");
             return Page();
         }
-        await emailSender.SendEmailAsync(
-            Input.Email,
-            "Confirm your email",
-            $"Please confirm your account by clicking here.");
+        else
+        {
+            switch (response.ErrorCodes?.Length)
+            {
+                case 0:
+                    throw new InvalidOperationException("The HCaptcha determined the passcode is not valid, and does not meet the security criteria");
+                case > 1:
+                    throw new InvalidOperationException("HCaptcha returned error codes: " + string.Join(", ", response.ErrorCodes));
+                default:
+                    {
+                        if (response.ErrorCodes is null)
+                        {
+                            throw new InvalidOperationException("HCaptcha returned error codes unexpectedly null");
+                        }
+                        if (HCaptchaErrorDetails.TryGetValue(response.ErrorCodes.Single(), out HCaptchaErrorDetails? details))
+                        {
+                            switch (details.ErrorCode)
+                            {
+                                case HCaptchaErrorDetails.MissingInputResponse:
+                                case HCaptchaErrorDetails.InvalidInputResponse:
+                                case HCaptchaErrorDetails.InvalidOrAlreadySeenResponse:
+                                    ModelState.AddModelError(string.Empty, details.FriendlyDescription);
+                                    break;
+                                case HCaptchaErrorDetails.BadRequest:
+                                    ModelState.AddModelError(string.Empty, details.FriendlyDescription);
+                                    break;
+                                case HCaptchaErrorDetails.MissingInputSecret:
+                                case HCaptchaErrorDetails.InvalidInputSecret:
+                                case HCaptchaErrorDetails.NotUsingDummyPasscode:
+                                case HCaptchaErrorDetails.SitekeySecretMismatch:
+                                    break;
+                                default:
+                                    throw new InvalidOperationException("HCaptcha returned unknown error code: " + details?.ErrorCode);
+                            }
+                        }
+                        else
+                        {
+                            throw new InvalidOperationException("HCaptcha returned unknown error code: " + response.ErrorCodes.Single());
+                        }
+
+                        break;
+                    }
+
+            }
+        }
 
-        ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email. If you can't find the email, please check your spam folder.");
         return Page();
     }
 }
diff --git a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml
index e430d01e..23749f1d 100644
--- a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml
+++ b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml
@@ -27,11 +27,15 @@
                 
                 
             
+            
             
         
     
 
 
 @section Scripts {
+    
     
 }
diff --git a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs
index 383de416..dcc1e30a 100644
--- a/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs
+++ b/EssentialCSharp.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs
@@ -1,15 +1,19 @@
 using System.ComponentModel.DataAnnotations;
 using System.Text;
 using EssentialCSharp.Web.Areas.Identity.Data;
+using EssentialCSharp.Web.Models;
+using EssentialCSharp.Web.Services;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.RazorPages;
 using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Options;
 
 namespace EssentialCSharp.Web.Areas.Identity.Pages.Account;
 
-public class ResetPasswordModel(UserManager userManager) : PageModel
+public class ResetPasswordModel(UserManager userManager, ICaptchaService captchaService, IOptions optionsAccessor) : PageModel
 {
+    public CaptchaOptions CaptchaOptions { get; } = optionsAccessor.Value;
     private InputModel? _Input;
     [BindProperty]
     public InputModel Input
@@ -57,44 +61,109 @@ public IActionResult OnGet(string? code = null)
 
     public async Task OnPostAsync()
     {
+        string? hCaptcha_response = Request.Form[CaptchaOptions.HttpPostResponseKeyName];
+
         if (!ModelState.IsValid)
         {
             return Page();
         }
 
-        if (Input.Email is null)
+        if (hCaptcha_response is null)
         {
-            ModelState.AddModelError(string.Empty, "Error: Email is required.");
-            return RedirectToPage();
+            ModelState.AddModelError(CaptchaOptions.HttpPostResponseKeyName, HCaptchaErrorDetails.GetValue(HCaptchaErrorDetails.MissingInputResponse).FriendlyDescription);
+            return Page();
         }
-        EssentialCSharpWebUser? user = await userManager.FindByEmailAsync(Input.Email);
-        if (user is null)
+
+        HCaptchaResult? response = await captchaService.VerifyAsync(hCaptcha_response);
+        if (response is null)
         {
-            // Don't reveal that the user does not exist
-            return RedirectToPage("./ResetPasswordConfirmation");
+            ModelState.AddModelError(CaptchaOptions.HttpPostResponseKeyName, "Error: HCaptcha API response unexpectedly null");
+            return Page();
         }
 
-        if (Input.Password is null)
+        if (response.Success)
         {
-            ModelState.AddModelError(string.Empty, "Error: Password is required.");
-            return RedirectToPage();
+            if (Input.Email is null)
+            {
+                ModelState.AddModelError(string.Empty, "Error: Email is required.");
+                return RedirectToPage();
+            }
+            EssentialCSharpWebUser? user = await userManager.FindByEmailAsync(Input.Email);
+            if (user is null)
+            {
+                // Don't reveal that the user does not exist
+                return RedirectToPage("./ResetPasswordConfirmation");
+            }
+
+            if (Input.Password is null)
+            {
+                ModelState.AddModelError(string.Empty, "Error: Password is required.");
+                return RedirectToPage();
+            }
+            if (Input.Code is null)
+            {
+                ModelState.AddModelError(string.Empty, "Error: Code is required.");
+                return RedirectToPage();
+            }
+
+            IdentityResult result = await userManager.ResetPasswordAsync(user, Input.Code, Input.Password);
+            if (result.Succeeded)
+            {
+                return RedirectToPage("./ResetPasswordConfirmation");
+            }
+
+            foreach (IdentityError error in result.Errors)
+            {
+                ModelState.AddModelError(string.Empty, error.Description);
+            }
+            return Page();
         }
-        if (Input.Code is null)
+        else
         {
-            ModelState.AddModelError(string.Empty, "Error: Code is required.");
-            return RedirectToPage();
-        }
+            switch (response.ErrorCodes?.Length)
+            {
+                case 0:
+                    throw new InvalidOperationException("The HCaptcha determined the passcode is not valid, and does not meet the security criteria");
+                case > 1:
+                    throw new InvalidOperationException("HCaptcha returned error codes: " + string.Join(", ", response.ErrorCodes));
+                default:
+                    {
+                        if (response.ErrorCodes is null)
+                        {
+                            throw new InvalidOperationException("HCaptcha returned error codes unexpectedly null");
+                        }
+                        if (HCaptchaErrorDetails.TryGetValue(response.ErrorCodes.Single(), out HCaptchaErrorDetails? details))
+                        {
+                            switch (details.ErrorCode)
+                            {
+                                case HCaptchaErrorDetails.MissingInputResponse:
+                                case HCaptchaErrorDetails.InvalidInputResponse:
+                                case HCaptchaErrorDetails.InvalidOrAlreadySeenResponse:
+                                    ModelState.AddModelError(string.Empty, details.FriendlyDescription);
+                                    break;
+                                case HCaptchaErrorDetails.BadRequest:
+                                    ModelState.AddModelError(string.Empty, details.FriendlyDescription);
+                                    break;
+                                case HCaptchaErrorDetails.MissingInputSecret:
+                                case HCaptchaErrorDetails.InvalidInputSecret:
+                                case HCaptchaErrorDetails.NotUsingDummyPasscode:
+                                case HCaptchaErrorDetails.SitekeySecretMismatch:
+                                    break;
+                                default:
+                                    throw new InvalidOperationException("HCaptcha returned unknown error code: " + details?.ErrorCode);
+                            }
+                        }
+                        else
+                        {
+                            throw new InvalidOperationException("HCaptcha returned unknown error code: " + response.ErrorCodes.Single());
+                        }
 
-        IdentityResult result = await userManager.ResetPasswordAsync(user, Input.Code, Input.Password);
-        if (result.Succeeded)
-        {
-            return RedirectToPage("./ResetPasswordConfirmation");
-        }
+                        break;
+                    }
 
-        foreach (IdentityError error in result.Errors)
-        {
-            ModelState.AddModelError(string.Empty, error.Description);
+            }
         }
+
         return Page();
     }
 }