diff --git a/.gitignore b/.gitignore
index cd94f9ce..853ec927 100644
--- a/.gitignore
+++ b/.gitignore
@@ -276,3 +276,4 @@ __pycache__/
Server/wwwroot/Downloads/Win-x64/Remotely_Desktop.exe
Server/wwwroot/Downloads/Win-x86/Remotely_Desktop.exe
/ScreenCast.Win/Properties/launchSettings.json
+/Server/ScaffoldingReadMe.txt
diff --git a/Server/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/Server/Areas/Identity/Pages/Account/ConfirmEmail.cshtml
new file mode 100644
index 00000000..beb1acbd
--- /dev/null
+++ b/Server/Areas/Identity/Pages/Account/ConfirmEmail.cshtml
@@ -0,0 +1,8 @@
+@page
+@model ConfirmEmailModel
+@{
+ ViewData["Title"] = "Confirm email";
+}
+
+
@ViewData["Title"]
+
\ No newline at end of file
diff --git a/Server/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/Server/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs
new file mode 100644
index 00000000..fb2c459a
--- /dev/null
+++ b/Server/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.WebUtilities;
+using Remotely.Shared.Models;
+
+namespace Remotely.Server.Areas.Identity.Pages.Account
+{
+ [AllowAnonymous]
+ public class ConfirmEmailModel : PageModel
+ {
+ private readonly UserManager _userManager;
+
+ public ConfirmEmailModel(UserManager userManager)
+ {
+ _userManager = userManager;
+ }
+
+ [TempData]
+ public string StatusMessage { get; set; }
+
+ public async Task OnGetAsync(string userId, string code)
+ {
+ if (userId == null || code == null)
+ {
+ return RedirectToPage("/Index");
+ }
+
+ var user = await _userManager.FindByIdAsync(userId);
+ if (user == null)
+ {
+ return NotFound($"Unable to load user with ID '{userId}'.");
+ }
+
+ code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
+ var result = await _userManager.ConfirmEmailAsync(user, code);
+ StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email.";
+ return Page();
+ }
+ }
+}
diff --git a/Server/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml b/Server/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml
new file mode 100644
index 00000000..cbe52758
--- /dev/null
+++ b/Server/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml
@@ -0,0 +1,8 @@
+@page
+@model ConfirmEmailChangeModel
+@{
+ ViewData["Title"] = "Confirm email change";
+}
+
+@ViewData["Title"]
+
\ No newline at end of file
diff --git a/Server/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs b/Server/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs
new file mode 100644
index 00000000..46095a9f
--- /dev/null
+++ b/Server/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.WebUtilities;
+using Remotely.Shared.Models;
+
+namespace Remotely.Server.Areas.Identity.Pages.Account
+{
+ [AllowAnonymous]
+ public class ConfirmEmailChangeModel : PageModel
+ {
+ private readonly UserManager _userManager;
+ private readonly SignInManager _signInManager;
+
+ public ConfirmEmailChangeModel(UserManager userManager, SignInManager signInManager)
+ {
+ _userManager = userManager;
+ _signInManager = signInManager;
+ }
+
+ [TempData]
+ public string StatusMessage { get; set; }
+
+ public async Task OnGetAsync(string userId, string email, string code)
+ {
+ if (userId == null || email == null || code == null)
+ {
+ return RedirectToPage("/Index");
+ }
+
+ var user = await _userManager.FindByIdAsync(userId);
+ if (user == null)
+ {
+ return NotFound($"Unable to load user with ID '{userId}'.");
+ }
+
+ code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
+ var result = await _userManager.ChangeEmailAsync(user, email, code);
+ if (!result.Succeeded)
+ {
+ StatusMessage = "Error changing email.";
+ return Page();
+ }
+
+ // In our UI email and user name are one and the same, so when we update the email
+ // we need to update the user name.
+ var setUserNameResult = await _userManager.SetUserNameAsync(user, email);
+ if (!setUserNameResult.Succeeded)
+ {
+ StatusMessage = "Error changing user name.";
+ return Page();
+ }
+
+ await _signInManager.RefreshSignInAsync(user);
+ StatusMessage = "Thank you for confirming your email change.";
+ return Page();
+ }
+ }
+}
diff --git a/Server/Areas/Identity/Pages/Account/_StatusMessage.cshtml b/Server/Areas/Identity/Pages/Account/_StatusMessage.cshtml
new file mode 100644
index 00000000..e9968413
--- /dev/null
+++ b/Server/Areas/Identity/Pages/Account/_StatusMessage.cshtml
@@ -0,0 +1,10 @@
+@model string
+
+@if (!String.IsNullOrEmpty(Model))
+{
+ var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
+
+
+ @Model
+
+}
diff --git a/Server/Pages/Shared/_LoginPartial.cshtml b/Server/Pages/Shared/_LoginPartial.cshtml
index c779a250..2b6c4b54 100644
--- a/Server/Pages/Shared/_LoginPartial.cshtml
+++ b/Server/Pages/Shared/_LoginPartial.cshtml
@@ -19,7 +19,7 @@
}
else
{
- if (DataService.GetOrganizationCount() < AppConfig.MaxOrganizationCount)
+ if (AppConfig.MaxOrganizationCount < 0 || DataService.GetOrganizationCount() < AppConfig.MaxOrganizationCount)
{
Register
diff --git a/Server/Pages/_IndexNotLoggedIn.cshtml b/Server/Pages/_IndexNotLoggedIn.cshtml
index 35dc8f23..4015bf12 100644
--- a/Server/Pages/_IndexNotLoggedIn.cshtml
+++ b/Server/Pages/_IndexNotLoggedIn.cshtml
@@ -23,7 +23,7 @@
Login
- @if (organizationCount < 0 || organizationCount < AppConfig.MaxOrganizationCount)
+ @if (AppConfig.MaxOrganizationCount < 0 || organizationCount < AppConfig.MaxOrganizationCount)
{