Your email has been successfully verified. You may now close this tab.
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs b/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs
new file mode 100644
index 00000000..8552b7ba
--- /dev/null
+++ b/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs
@@ -0,0 +1,39 @@
+#nullable enable
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using LBPUnion.ProjectLighthouse.Pages.Layouts;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Profiles.Email;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Pages;
+
+public class CompleteEmailVerificationPage : BaseLayout
+{
+ public CompleteEmailVerificationPage([NotNull] Database database) : base(database)
+ {}
+
+ public string? Error = null;
+
+ public async Task OnGet(string token)
+ {
+ User? user = this.Database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("~/login");
+
+ EmailVerificationToken? emailVerifyToken = await this.Database.EmailVerificationTokens.FirstOrDefaultAsync(e => e.EmailToken == token);
+ if (emailVerifyToken == null)
+ {
+ this.Error = "Invalid verification token";
+ return this.Page();
+ }
+
+ this.Database.EmailVerificationTokens.Remove(emailVerifyToken);
+
+ user.EmailAddressVerified = true;
+
+ await this.Database.SaveChangesAsync();
+
+ return this.Page();
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs
index babb65d6..98dfdf67 100644
--- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs
+++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs
@@ -105,6 +105,7 @@ public class LoginForm : BaseLayout
Logger.Log($"User {user.Username} (id: {user.UserId}) successfully logged in on web", LoggerLevelLogin.Instance);
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
+ if (!user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail");
return this.RedirectToPage(nameof(LandingPage));
}
diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs
index 51649f72..62b5129e 100644
--- a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs
+++ b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs
@@ -38,6 +38,8 @@ public class PasswordResetPage : BaseLayout
await this.Database.SaveChangesAsync();
+ if (!user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail");
+
return this.Redirect("~/");
}
diff --git a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml b/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml
new file mode 100644
index 00000000..09e0f2dc
--- /dev/null
+++ b/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml
@@ -0,0 +1,14 @@
+@page "/login/sendVerificationEmail"
+@model LBPUnion.ProjectLighthouse.Pages.SendVerificationEmailPage
+
+@{
+ Layout = "Layouts/BaseLayout";
+ Model.Title = "Verify Email Address";
+}
+
+An email address on your account has been set, but hasn't been verified yet.
+To verify it, check the email sent to @Model.User.EmailAddress and click the link in the email.
+
+
+ Resend email
+
\ No newline at end of file
diff --git a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs b/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs
new file mode 100644
index 00000000..f02a2244
--- /dev/null
+++ b/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs
@@ -0,0 +1,47 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Pages.Layouts;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Profiles.Email;
+using LBPUnion.ProjectLighthouse.Types.Settings;
+using Microsoft.AspNetCore.Mvc;
+
+namespace LBPUnion.ProjectLighthouse.Pages;
+
+public class SendVerificationEmailPage : BaseLayout
+{
+ public SendVerificationEmailPage(Database database) : base(database)
+ {}
+
+ public async Task OnGet()
+ {
+ User? user = this.Database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("/login");
+
+ EmailVerificationToken verifyToken = new()
+ {
+ UserId = user.UserId,
+ User = user,
+ EmailToken = HashHelper.GenerateAuthToken(),
+ };
+
+ this.Database.EmailVerificationTokens.Add(verifyToken);
+
+ await this.Database.SaveChangesAsync();
+
+ string body = "Hello,\n\n" +
+ $"A request to verify this email for your Project Lighthouse account ({user.Username}).\n\n" +
+ $"To verify your account, click this link: {ServerSettings.Instance.ExternalUrl}/verifyEmail?token={verifyToken.EmailToken}";
+
+ if (SMTPHelper.SendEmail(user.EmailAddress, "Project Lighthouse Email Verification", body))
+ {
+ return this.Page();
+ }
+ else
+ {
+ throw new Exception("failed to send email");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs b/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs
index bd46778e..dfcb3c3e 100644
--- a/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs
+++ b/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs
@@ -1,8 +1,13 @@
#nullable enable
+using System;
using System.Threading.Tasks;
+using Kettu;
using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
+using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles.Email;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -35,17 +40,39 @@ public class SetEmailForm : BaseLayout
emailToken.User.EmailAddress = emailAddress;
this.Database.EmailSetTokens.Remove(emailToken);
+ User user = emailToken.User;
+
EmailVerificationToken emailVerifyToken = new()
{
- UserId = emailToken.UserId,
- User = emailToken.User,
+ UserId = user.UserId,
+ User = user,
EmailToken = HashHelper.GenerateAuthToken(),
};
this.Database.EmailVerificationTokens.Add(emailVerifyToken);
+ // The user just set their email address. Now, let's grant them a token to proceed with verifying the email.
+ WebToken webToken = new()
+ {
+ UserId = user.UserId,
+ UserToken = HashHelper.GenerateAuthToken(),
+ };
+
+ this.Response.Cookies.Append
+ (
+ "LighthouseToken",
+ webToken.UserToken,
+ new CookieOptions
+ {
+ Expires = DateTimeOffset.Now.AddDays(7),
+ }
+ );
+
+ Logger.Log($"User {user.Username} (id: {user.UserId}) successfully logged in on web after setting an email address", LoggerLevelLogin.Instance);
+
+ this.Database.WebTokens.Add(webToken);
await this.Database.SaveChangesAsync();
- return this.Redirect("/login/verify?token=" + emailVerifyToken.EmailToken);
+ return this.Redirect("/login/sendVerificationEmail");
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs
index 35e7a7cc..6356d75c 100644
--- a/ProjectLighthouse/Types/User.cs
+++ b/ProjectLighthouse/Types/User.cs
@@ -18,6 +18,8 @@ public class User
public string? EmailAddress { get; set; } = null;
#nullable disable
+ public bool EmailAddressVerified { get; set; } = false;
+
[JsonIgnore]
public string Password { get; set; }