This commit integrates ASP.NET Core Identity into the application to enable user registration, login, and management. This lays the groundwork for securing data per user.
**Key Changes:**
* **DbContext Configuration:**
* Modified `ApplicationDbContext.cs` to inherit from `IdentityDbContext<IdentityUser>`.
* Removed an unnecessary `using` statement from `ApplicationDbContext.cs`.
* **Program.cs Setup:**
* Configured `AddDefaultIdentity<IdentityUser>` with `AddEntityFrameworkStores<ApplicationDbContext>()` to register Identity services.
* Ensured correct ordering of `UseAuthentication()` and `UseAuthorization()` middleware.
* Added `app.MapRazorPages()` to enable the Identity UI pages.
* Verified core package versions in `turf_tasker.csproj` for consistency across EF Core and Identity components (`8.0.6`).
* **Identity UI:**
* Scaffolded ASP.NET Core Identity pages (Login, Register, Manage, etc.) to provide the user interface for authentication.
* Added a `_LoginPartial.cshtml` partial view to the `Views/Shared` folder.
* Rendered `_LoginPartial` in `Views/Shared/_Layout.cshtml` to display login/register/logout links in the navigation bar.
* **Migrations:**
* Created and applied a new migration (`AddIdentitySchema`) to create the necessary ASP.NET Core Identity database tables (e.g., `AspNetUsers`, `AspNetRoles`).
417 lines
15 KiB
Text
417 lines
15 KiB
Text
diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs
|
|
index e919f1a..0f29688 100644
|
|
--- a/Data/ApplicationDbContext.cs
|
|
+++ b/Data/ApplicationDbContext.cs
|
|
@@ -1,12 +1,20 @@
|
|
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
|
+using Microsoft.AspNetCore.Identity;
|
|
+using Microsoft.AspNetCore.Identity.UI;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using turf_tasker.Models;
|
|
|
|
namespace turf_tasker.Data;
|
|
|
|
-public class ApplicationDbContext : DbContext
|
|
+public class ApplicationDbContext : IdentityDbContext<IdentityUser>
|
|
{
|
|
+ private readonly DbContextOptions<ApplicationDbContext> _options;
|
|
+
|
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
|
- : base(options) { }
|
|
+ : base(options)
|
|
+ {
|
|
+ _options = options;
|
|
+ }
|
|
|
|
public DbSet<LawnCareEvent> LawnCareEvents { get; set; }
|
|
|
|
diff --git a/Migrations/ApplicationDbContextModelSnapshot.cs b/Migrations/ApplicationDbContextModelSnapshot.cs
|
|
index 320c9f9..c57d1dc 100644
|
|
--- a/Migrations/ApplicationDbContextModelSnapshot.cs
|
|
+++ b/Migrations/ApplicationDbContextModelSnapshot.cs
|
|
@@ -15,7 +15,203 @@ namespace turf_tasker.Migrations
|
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
{
|
|
#pragma warning disable 612, 618
|
|
- modelBuilder.HasAnnotation("ProductVersion", "9.0.6");
|
|
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
|
+ {
|
|
+ b.Property<string>("Id")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("ConcurrencyStamp")
|
|
+ .IsConcurrencyToken()
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("Name")
|
|
+ .HasMaxLength(256)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("NormalizedName")
|
|
+ .HasMaxLength(256)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("Id");
|
|
+
|
|
+ b.HasIndex("NormalizedName")
|
|
+ .IsUnique()
|
|
+ .HasDatabaseName("RoleNameIndex");
|
|
+
|
|
+ b.ToTable("AspNetRoles", (string)null);
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
|
+ {
|
|
+ b.Property<int>("Id")
|
|
+ .ValueGeneratedOnAdd()
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<string>("ClaimType")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("ClaimValue")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("RoleId")
|
|
+ .IsRequired()
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("Id");
|
|
+
|
|
+ b.HasIndex("RoleId");
|
|
+
|
|
+ b.ToTable("AspNetRoleClaims", (string)null);
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
|
+ {
|
|
+ b.Property<string>("Id")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<int>("AccessFailedCount")
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<string>("ConcurrencyStamp")
|
|
+ .IsConcurrencyToken()
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("Email")
|
|
+ .HasMaxLength(256)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<bool>("EmailConfirmed")
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<bool>("LockoutEnabled")
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<DateTimeOffset?>("LockoutEnd")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("NormalizedEmail")
|
|
+ .HasMaxLength(256)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("NormalizedUserName")
|
|
+ .HasMaxLength(256)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("PasswordHash")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("PhoneNumber")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<bool>("PhoneNumberConfirmed")
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<string>("SecurityStamp")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<bool>("TwoFactorEnabled")
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<string>("UserName")
|
|
+ .HasMaxLength(256)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("Id");
|
|
+
|
|
+ b.HasIndex("NormalizedEmail")
|
|
+ .HasDatabaseName("EmailIndex");
|
|
+
|
|
+ b.HasIndex("NormalizedUserName")
|
|
+ .IsUnique()
|
|
+ .HasDatabaseName("UserNameIndex");
|
|
+
|
|
+ b.ToTable("AspNetUsers", (string)null);
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
|
+ {
|
|
+ b.Property<int>("Id")
|
|
+ .ValueGeneratedOnAdd()
|
|
+ .HasColumnType("INTEGER");
|
|
+
|
|
+ b.Property<string>("ClaimType")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("ClaimValue")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("UserId")
|
|
+ .IsRequired()
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("Id");
|
|
+
|
|
+ b.HasIndex("UserId");
|
|
+
|
|
+ b.ToTable("AspNetUserClaims", (string)null);
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
|
+ {
|
|
+ b.Property<string>("LoginProvider")
|
|
+ .HasMaxLength(128)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("ProviderKey")
|
|
+ .HasMaxLength(128)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("ProviderDisplayName")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("UserId")
|
|
+ .IsRequired()
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("LoginProvider", "ProviderKey");
|
|
+
|
|
+ b.HasIndex("UserId");
|
|
+
|
|
+ b.ToTable("AspNetUserLogins", (string)null);
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
|
+ {
|
|
+ b.Property<string>("UserId")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("RoleId")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("UserId", "RoleId");
|
|
+
|
|
+ b.HasIndex("RoleId");
|
|
+
|
|
+ b.ToTable("AspNetUserRoles", (string)null);
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
|
+ {
|
|
+ b.Property<string>("UserId")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("LoginProvider")
|
|
+ .HasMaxLength(128)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("Name")
|
|
+ .HasMaxLength(128)
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.Property<string>("Value")
|
|
+ .HasColumnType("TEXT");
|
|
+
|
|
+ b.HasKey("UserId", "LoginProvider", "Name");
|
|
+
|
|
+ b.ToTable("AspNetUserTokens", (string)null);
|
|
+ });
|
|
|
|
modelBuilder.Entity("turf_tasker.Models.LawnCareEvent", b =>
|
|
{
|
|
@@ -77,6 +273,57 @@ namespace turf_tasker.Migrations
|
|
|
|
b.ToTable("LawnCareTips");
|
|
});
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
|
+ {
|
|
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
|
+ .WithMany()
|
|
+ .HasForeignKey("RoleId")
|
|
+ .OnDelete(DeleteBehavior.Cascade)
|
|
+ .IsRequired();
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
|
+ {
|
|
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
|
+ .WithMany()
|
|
+ .HasForeignKey("UserId")
|
|
+ .OnDelete(DeleteBehavior.Cascade)
|
|
+ .IsRequired();
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
|
+ {
|
|
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
|
+ .WithMany()
|
|
+ .HasForeignKey("UserId")
|
|
+ .OnDelete(DeleteBehavior.Cascade)
|
|
+ .IsRequired();
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
|
+ {
|
|
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
|
+ .WithMany()
|
|
+ .HasForeignKey("RoleId")
|
|
+ .OnDelete(DeleteBehavior.Cascade)
|
|
+ .IsRequired();
|
|
+
|
|
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
|
+ .WithMany()
|
|
+ .HasForeignKey("UserId")
|
|
+ .OnDelete(DeleteBehavior.Cascade)
|
|
+ .IsRequired();
|
|
+ });
|
|
+
|
|
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
|
+ {
|
|
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
|
+ .WithMany()
|
|
+ .HasForeignKey("UserId")
|
|
+ .OnDelete(DeleteBehavior.Cascade)
|
|
+ .IsRequired();
|
|
+ });
|
|
#pragma warning restore 612, 618
|
|
}
|
|
}
|
|
diff --git a/Program.cs b/Program.cs
|
|
index dcf9de2..701e4e2 100644
|
|
--- a/Program.cs
|
|
+++ b/Program.cs
|
|
@@ -1,20 +1,22 @@
|
|
using Microsoft.EntityFrameworkCore;
|
|
using turf_tasker.Data;
|
|
+using Microsoft.AspNetCore.Identity;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
-// Add services to the container.
|
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
options.UseSqlite(
|
|
builder.Configuration.GetConnectionString("DefaultConnection")
|
|
)
|
|
);
|
|
|
|
+builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
|
+ .AddEntityFrameworkStores<ApplicationDbContext>();
|
|
+
|
|
builder.Services.AddControllersWithViews();
|
|
|
|
var app = builder.Build();
|
|
|
|
-// Configure the HTTP request pipeline.
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseExceptionHandler("/Home/Error");
|
|
@@ -26,6 +28,7 @@ app.UseStaticFiles();
|
|
|
|
app.UseRouting();
|
|
|
|
+app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapControllerRoute(
|
|
@@ -33,4 +36,6 @@ app.MapControllerRoute(
|
|
pattern: "{controller=Home}/{action=Index}/{id?}"
|
|
);
|
|
|
|
+app.MapRazorPages();
|
|
+
|
|
app.Run();
|
|
\ No newline at end of file
|
|
diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml
|
|
index c27a958..f67aa24 100644
|
|
--- a/Views/Shared/_Layout.cshtml
|
|
+++ b/Views/Shared/_Layout.cshtml
|
|
@@ -33,6 +33,7 @@
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
+ <partial name="_LoginPartial" />
|
|
</header>
|
|
<div class="container">
|
|
<main role="main" class="pb-3">
|
|
diff --git a/Views/Shared/_LoginPartial.cshtml b/Views/Shared/_LoginPartial.cshtml
|
|
index e69de29..b838e04 100644
|
|
--- a/Views/Shared/_LoginPartial.cshtml
|
|
+++ b/Views/Shared/_LoginPartial.cshtml
|
|
@@ -0,0 +1,26 @@
|
|
+@using Microsoft.AspNetCore.Identity
|
|
+@inject SignInManager<IdentityUser> SignInManager
|
|
+@inject UserManager<IdentityUser> UserManager
|
|
+
|
|
+<ul class="navbar-nav">
|
|
+ @if (SignInManager.IsSignedIn(User))
|
|
+ {
|
|
+ <li class="nav-item">
|
|
+ <a id="manage" class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
|
|
+ </li>
|
|
+ <li class="nav-item">
|
|
+ <form id="logoutForm" class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
|
|
+ <button id="logout" type="submit" class="nav-link btn btn-link text-dark">Logout</button>
|
|
+ </form>
|
|
+ </li>
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ <li class="nav-item">
|
|
+ <a class="nav-link text-dark" id="register" asp-area="Identity" asp-page="/Account/Register">Register</a>
|
|
+ </li>
|
|
+ <li class="nav-item">
|
|
+ <a class="nav-link text-dark" id="login" asp-area="Identity" asp-page="/Account/Login">Login</a>
|
|
+ </li>
|
|
+ }
|
|
+</ul>
|
|
\ No newline at end of file
|
|
diff --git a/turf_tasker.csproj b/turf_tasker.csproj
|
|
index dbd9ba1..2247d16 100644
|
|
--- a/turf_tasker.csproj
|
|
+++ b/turf_tasker.csproj
|
|
@@ -7,13 +7,22 @@
|
|
</PropertyGroup>
|
|
|
|
<ItemGroup>
|
|
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" />
|
|
- <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" />
|
|
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.6">
|
|
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
- <PrivateAssets>all</PrivateAssets>
|
|
- </PackageReference>
|
|
- <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
|
+ <!-- Identity Packages -->
|
|
+ <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.6" />
|
|
+ <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.6" />
|
|
+
|
|
+ <!-- Explicitly define ALL core EF Core packages to force version alignment -->
|
|
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
|
|
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.6" />
|
|
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
|
+ <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
|
|
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6">
|
|
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
+ <PrivateAssets>all</PrivateAssets>
|
|
+ </PackageReference>
|
|
+
|
|
+ <!-- Scaffolding Package -->
|
|
+ <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
|
</ItemGroup>
|
|
|
|
</Project>
|