diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Login.cshtml b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Login.cshtml new file mode 100644 index 0000000..661370f --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Login.cshtml @@ -0,0 +1,83 @@ +@page +@model LoginModel + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"]

+
+
+
+
+

Use a local account to log in.

+
+ +
+ + + +
+
+ + + +
+
+ +
+
+ +
+
+

+ Forgot your password? +

+

+ Register as a new user +

+

+ Resend email confirmation +

+
+
+
+
+
+
+

Use another service to log in.

+
+ @{ + if ((Model.ExternalLogins?.Count ?? 0) == 0) + { +
+

+ There are no external authentication services configured. See this article + about setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in Model.ExternalLogins!) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + +} diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Login.cshtml.cs b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Login.cshtml.cs new file mode 100644 index 0000000..6780ed2 --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using SpeakingInBitsWeb.Models; + +namespace SpeakingInBitsWeb.Areas.Identity.Pages.Account +{ + public class LoginModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [BindProperty] + public InputModel Input { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public IList ExternalLogins { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public string ReturnUrl { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [TempData] + public string ErrorMessage { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public class InputModel + { + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [Required] + public string Username { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + + public async Task OnGetAsync(string returnUrl = null) + { + if (!string.IsNullOrEmpty(ErrorMessage)) + { + ModelState.AddModelError(string.Empty, ErrorMessage); + } + + returnUrl ??= Url.Content("~/"); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl ??= Url.Content("~/"); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberMe, lockoutOnFailure: false); + 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(); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + } +} diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Register.cshtml b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Register.cshtml new file mode 100644 index 0000000..323ed57 --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Register.cshtml @@ -0,0 +1,82 @@ +@page +@model RegisterModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+ +
+
+
+

Create a new account.

+
+ +
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+
+

Use another service to register.

+
+ @{ + if ((Model.ExternalLogins?.Count ?? 0) == 0) + { +
+

+ There are no external authentication services configured. See this article + about setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in Model.ExternalLogins!) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + +} diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Register.cshtml.cs b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Register.cshtml.cs new file mode 100644 index 0000000..e8ab1e9 --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -0,0 +1,199 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +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.Logging; +using SpeakingInBitsWeb.Models; + +namespace SpeakingInBitsWeb.Areas.Identity.Pages.Account +{ + public class RegisterModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly IUserStore _userStore; + private readonly IUserEmailStore _emailStore; + private readonly ILogger _logger; + private readonly IEmailSender _emailSender; + + public RegisterModel( + UserManager userManager, + IUserStore userStore, + SignInManager signInManager, + ILogger logger, + IEmailSender emailSender) + { + _userManager = userManager; + _userStore = userStore; + _emailStore = GetEmailStore(); + _signInManager = signInManager; + _logger = logger; + _emailSender = emailSender; + } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [BindProperty] + public InputModel Input { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public string ReturnUrl { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public IList ExternalLogins { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public class InputModel + { + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [RegularExpression(@"^[a-zA-Z0-9]*$", ErrorMessage = "Username can only contain letters and numbers.")] + public string Username { get; set; } + + [Required] + [StringLength(50)] + [Display(Name = "First Name")] + public string FirstName { get; set; } + + [Required] + [StringLength(50)] + [Display(Name = "Last Name")] + public string LastName { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + /// + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + + public async Task OnGetAsync(string returnUrl = null) + { + ReturnUrl = returnUrl; + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl ??= Url.Content("~/"); + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + if (ModelState.IsValid) + { + var user = CreateUser(); + + // Populate the custom properties + user.FirstName = Input.FirstName; + user.LastName = Input.LastName; + + await _userStore.SetUserNameAsync(user, Input.Username, CancellationToken.None); + await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + var result = await _userManager.CreateAsync(user, Input.Password); + + if (result.Succeeded) + { + _logger.LogInformation("User created a new account with password."); + + var userId = await _userManager.GetUserIdAsync(user); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + if (_userManager.Options.SignIn.RequireConfirmedAccount) + { + return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); + } + else + { + await _signInManager.SignInAsync(user, isPersistent: false); + return LocalRedirect(returnUrl); + } + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + + private ApplicationUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " + + $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor, or alternatively " + + $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml"); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!_userManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)_userStore; + } + } +} diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/Account/_ViewImports.cshtml b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/_ViewImports.cshtml new file mode 100644 index 0000000..a1dd2e8 --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/Account/_ViewImports.cshtml @@ -0,0 +1 @@ +@using SpeakingInBitsWeb.Areas.Identity.Pages.Account \ No newline at end of file diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/SpeakingInBitsWeb/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml new file mode 100644 index 0000000..5d1f685 --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml @@ -0,0 +1,2 @@ + + diff --git a/SpeakingInBitsWeb/Areas/Identity/Pages/_ViewImports.cshtml b/SpeakingInBitsWeb/Areas/Identity/Pages/_ViewImports.cshtml new file mode 100644 index 0000000..6c18d56 --- /dev/null +++ b/SpeakingInBitsWeb/Areas/Identity/Pages/_ViewImports.cshtml @@ -0,0 +1,5 @@ +@using Microsoft.AspNetCore.Identity +@using SpeakingInBitsWeb.Areas.Identity +@using SpeakingInBitsWeb.Areas.Identity.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@using SpeakingInBitsWeb.Models diff --git a/SpeakingInBitsWeb/Data/ApplicationDbContext.cs b/SpeakingInBitsWeb/Data/ApplicationDbContext.cs index bcd098e..1c5003f 100644 --- a/SpeakingInBitsWeb/Data/ApplicationDbContext.cs +++ b/SpeakingInBitsWeb/Data/ApplicationDbContext.cs @@ -1,9 +1,25 @@ using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using SpeakingInBitsWeb.Models; namespace SpeakingInBitsWeb.Data { - public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) + public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) { + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity(entity => + { + entity.Property(e => e.FirstName) + .HasMaxLength(50) + .IsRequired(); + + entity.Property(e => e.LastName) + .HasMaxLength(50) + .IsRequired(); + }); + } } } diff --git a/SpeakingInBitsWeb/Data/Migrations/20251003175158_CustomizeUser.Designer.cs b/SpeakingInBitsWeb/Data/Migrations/20251003175158_CustomizeUser.Designer.cs new file mode 100644 index 0000000..6d3719c --- /dev/null +++ b/SpeakingInBitsWeb/Data/Migrations/20251003175158_CustomizeUser.Designer.cs @@ -0,0 +1,291 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SpeakingInBitsWeb.Data; + +#nullable disable + +namespace SpeakingInBitsWeb.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251003175158_CustomizeUser")] + partial class CustomizeUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0-rc.1.25451.107") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("SpeakingInBitsWeb.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SpeakingInBitsWeb/Data/Migrations/20251003175158_CustomizeUser.cs b/SpeakingInBitsWeb/Data/Migrations/20251003175158_CustomizeUser.cs new file mode 100644 index 0000000..00b0211 --- /dev/null +++ b/SpeakingInBitsWeb/Data/Migrations/20251003175158_CustomizeUser.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SpeakingInBitsWeb.Data.Migrations +{ + /// + public partial class CustomizeUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FirstName", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "LastName", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FirstName", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "LastName", + table: "AspNetUsers"); + } + } +} diff --git a/SpeakingInBitsWeb/Data/Migrations/20251003175653_AddedNameLength.Designer.cs b/SpeakingInBitsWeb/Data/Migrations/20251003175653_AddedNameLength.Designer.cs new file mode 100644 index 0000000..6719b24 --- /dev/null +++ b/SpeakingInBitsWeb/Data/Migrations/20251003175653_AddedNameLength.Designer.cs @@ -0,0 +1,293 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SpeakingInBitsWeb.Data; + +#nullable disable + +namespace SpeakingInBitsWeb.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251003175653_AddedNameLength")] + partial class AddedNameLength + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0-rc.1.25451.107") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("SpeakingInBitsWeb.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SpeakingInBitsWeb/Data/Migrations/20251003175653_AddedNameLength.cs b/SpeakingInBitsWeb/Data/Migrations/20251003175653_AddedNameLength.cs new file mode 100644 index 0000000..5365e86 --- /dev/null +++ b/SpeakingInBitsWeb/Data/Migrations/20251003175653_AddedNameLength.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SpeakingInBitsWeb.Data.Migrations +{ + /// + public partial class AddedNameLength : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "LastName", + table: "AspNetUsers", + type: "nvarchar(50)", + maxLength: 50, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "AspNetUsers", + type: "nvarchar(50)", + maxLength: 50, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "LastName", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(50)", + oldMaxLength: 50); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(50)", + oldMaxLength: 50); + } + } +} diff --git a/SpeakingInBitsWeb/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/SpeakingInBitsWeb/Data/Migrations/ApplicationDbContextModelSnapshot.cs index eecfeb5..83e4fc3 100644 --- a/SpeakingInBitsWeb/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/SpeakingInBitsWeb/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -6,6 +6,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using SpeakingInBitsWeb.Data; +#nullable disable + namespace SpeakingInBitsWeb.Data.Migrations { [DbContext(typeof(ApplicationDbContext))] @@ -15,9 +17,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "10.0.0-rc.1.25451.107") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -29,29 +32,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); - b.ToTable("AspNetRoles"); + b.ToTable("AspNetRoles", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -67,80 +71,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RoleId"); - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers"); + b.ToTable("AspNetRoleClaims", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -156,18 +96,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("AspNetUserClaims"); + b.ToTable("AspNetUserClaims", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -180,7 +120,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("AspNetUserLogins"); + b.ToTable("AspNetUserLogins", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => @@ -195,7 +135,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RoleId"); - b.ToTable("AspNetUserRoles"); + b.ToTable("AspNetUserRoles", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => @@ -204,19 +144,94 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); b.HasKey("UserId", "LoginProvider", "Name"); - b.ToTable("AspNetUserTokens"); + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("SpeakingInBitsWeb.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => @@ -230,7 +245,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -239,7 +254,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -254,7 +269,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -263,7 +278,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("SpeakingInBitsWeb.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) diff --git a/SpeakingInBitsWeb/Models/ApplicationUser.cs b/SpeakingInBitsWeb/Models/ApplicationUser.cs new file mode 100644 index 0000000..7d60de3 --- /dev/null +++ b/SpeakingInBitsWeb/Models/ApplicationUser.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Identity; + +namespace SpeakingInBitsWeb.Models +{ + /// + /// Represents a custom application user with additional profile information. + /// + /// Use this class to store and manage user-specific data beyond the default identity + /// fields. + public class ApplicationUser : IdentityUser + { + /// + /// The user's legal first name + /// + public required string FirstName { get; set; } + + /// + /// The user's legal last name + /// + public required string LastName { get; set; } + } +} diff --git a/SpeakingInBitsWeb/Program.cs b/SpeakingInBitsWeb/Program.cs index c7a4dff..fad9f1e 100644 --- a/SpeakingInBitsWeb/Program.cs +++ b/SpeakingInBitsWeb/Program.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using SpeakingInBitsWeb.Data; +using SpeakingInBitsWeb.Models; var builder = WebApplication.CreateBuilder(args); @@ -10,7 +11,7 @@ options.UseSqlServer(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); -builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) +builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores(); builder.Services.AddControllersWithViews(); diff --git a/SpeakingInBitsWeb/SpeakingInBitsWeb.csproj b/SpeakingInBitsWeb/SpeakingInBitsWeb.csproj index 0b6c26c..c2a5e4b 100644 --- a/SpeakingInBitsWeb/SpeakingInBitsWeb.csproj +++ b/SpeakingInBitsWeb/SpeakingInBitsWeb.csproj @@ -11,8 +11,10 @@ + + diff --git a/SpeakingInBitsWeb/Views/Shared/_LoginPartial.cshtml b/SpeakingInBitsWeb/Views/Shared/_LoginPartial.cshtml index 921dbe1..e3d30b0 100644 --- a/SpeakingInBitsWeb/Views/Shared/_LoginPartial.cshtml +++ b/SpeakingInBitsWeb/Views/Shared/_LoginPartial.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Identity -@inject SignInManager SignInManager -@inject UserManager UserManager +@inject SignInManager SignInManager +@inject UserManager UserManager