diff --git a/E-Commerce.Domain/E-Commerce.Domain.csproj b/E-Commerce.Domain/E-Commerce.Domain.csproj
index 8c5a65c..eae74cc 100644
--- a/E-Commerce.Domain/E-Commerce.Domain.csproj
+++ b/E-Commerce.Domain/E-Commerce.Domain.csproj
@@ -7,4 +7,8 @@
enable
+
+
+
+
diff --git a/E-Commerce.Domain/IdentityModule/Address.cs b/E-Commerce.Domain/IdentityModule/Address.cs
new file mode 100644
index 0000000..f2f64b6
--- /dev/null
+++ b/E-Commerce.Domain/IdentityModule/Address.cs
@@ -0,0 +1,15 @@
+namespace E_Commerce.Domain.IdentityModule
+{
+ public class Address
+ {
+ public int Id { get; set; } // PK
+ public string City { get; set; } = default!;
+ public string Street { get; set; } = default!;
+ public string Country { get; set; } = default!;
+ public string FirstName { get; set; } = default!;
+ public string LastName { get; set; } = default!;
+
+ public ApplicationUser User { get; set; } = default!;
+ public string UserId { get; set; } = default!;
+ }
+}
\ No newline at end of file
diff --git a/E-Commerce.Domain/IdentityModule/ApplicationUser.cs b/E-Commerce.Domain/IdentityModule/ApplicationUser.cs
new file mode 100644
index 0000000..7944095
--- /dev/null
+++ b/E-Commerce.Domain/IdentityModule/ApplicationUser.cs
@@ -0,0 +1,17 @@
+using Microsoft.AspNetCore.Identity;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Domain.IdentityModule
+{
+ public class ApplicationUser : IdentityUser
+ {
+
+ public string DisplayName { get; set; } = default!;
+ public Address? Address { get; set; }
+
+ }
+}
diff --git a/E-Commerce.Persistance/IdentityData/DbContexts/StoreIdentityDbContext.cs b/E-Commerce.Persistance/IdentityData/DbContexts/StoreIdentityDbContext.cs
new file mode 100644
index 0000000..bc550aa
--- /dev/null
+++ b/E-Commerce.Persistance/IdentityData/DbContexts/StoreIdentityDbContext.cs
@@ -0,0 +1,29 @@
+using E_Commerce.Domain.IdentityModule;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Persistance.IdentityData.DbContexts
+{
+ public class StoreIdentityDbContext : IdentityDbContext
+ {
+ public StoreIdentityDbContext(DbContextOptions options) : base(options)
+ {
+
+ }
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+ builder.Entity().ToTable("Addresses");
+ builder.Entity().ToTable("Users");
+ builder.Entity().ToTable("Roles");
+ builder.Entity>().ToTable("UserRoles");
+ }
+
+ }
+}
diff --git a/E-Commerce.Persistance/IdentityData/IdentityData/IdentityDataInitializer.cs b/E-Commerce.Persistance/IdentityData/IdentityData/IdentityDataInitializer.cs
new file mode 100644
index 0000000..023ac3a
--- /dev/null
+++ b/E-Commerce.Persistance/IdentityData/IdentityData/IdentityDataInitializer.cs
@@ -0,0 +1,68 @@
+using E_Commerce.Domain.Contracts;
+using E_Commerce.Domain.IdentityModule;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Persistance.IdentityData.IdentityData
+{
+ public class IdentityDataInitializer : IDataInitializer
+ {
+ private readonly UserManager _userManager;
+ private readonly RoleManager _roleManager;
+ private readonly ILogger _logger;
+
+ public IdentityDataInitializer(UserManager userManager,
+ RoleManager roleManager,
+ ILogger logger)
+ {
+ _userManager = userManager;
+ _roleManager = roleManager;
+ _logger = logger;
+ }
+ public async Task InitializeAsync()
+ {
+ try
+ {
+
+ if(!_roleManager.Roles.Any())
+ {
+ await _roleManager.CreateAsync(new IdentityRole("Admin"));
+ await _roleManager.CreateAsync(new IdentityRole("SuperAdmin"));
+ }
+
+ if(_userManager.Users.Any())
+ {
+ var User01 = new ApplicationUser()
+ {
+ DisplayName = "Mohamed Tarek" ,
+ UserName = "MohamedTarek" ,
+ Email = "MohamedTarek@gmail.com" ,
+ PhoneNumber = "0123456789"
+ };
+ var User02 = new ApplicationUser()
+ {
+ DisplayName = "Salma Tarek",
+ UserName = "SalmaTarek",
+ Email = "SalmaTarek@gmail.com",
+ PhoneNumber = "0123456559"
+ };
+
+ await _userManager.CreateAsync(User01, "P@ssw0rd");
+ await _userManager.CreateAsync(User02, "P@ssw0rd");
+
+ await _userManager.AddToRoleAsync(User01, "Admin");
+ await _userManager.AddToRoleAsync(User02, "SuperAdmin");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error During Seeding Identity DataBase : {ex}");
+ }
+ }
+ }
+}
diff --git a/E-Commerce.Persistance/IdentityData/Migrations/20251120174639_IdentityTablesInitialCreate.Designer.cs b/E-Commerce.Persistance/IdentityData/Migrations/20251120174639_IdentityTablesInitialCreate.Designer.cs
new file mode 100644
index 0000000..48ba385
--- /dev/null
+++ b/E-Commerce.Persistance/IdentityData/Migrations/20251120174639_IdentityTablesInitialCreate.Designer.cs
@@ -0,0 +1,339 @@
+//
+using System;
+using E_Commerce.Persistance.IdentityData.DbContexts;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace E_Commerce.Persistance.IdentityData.Migrations
+{
+ [DbContext(typeof(StoreIdentityDbContext))]
+ [Migration("20251120174639_IdentityTablesInitialCreate")]
+ partial class IdentityTablesInitialCreate
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.Address", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("City")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Country")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Street")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("Addresses", (string)null);
+ });
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.ApplicationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("bit");
+
+ 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("Users", (string)null);
+ });
+
+ 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("Roles", (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")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ProviderKey")
+ .HasColumnType("nvarchar(450)");
+
+ 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("UserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("LoginProvider")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Name")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Value")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.Address", b =>
+ {
+ b.HasOne("E_Commerce.Domain.IdentityModule.ApplicationUser", "User")
+ .WithOne("Address")
+ .HasForeignKey("E_Commerce.Domain.IdentityModule.Address", "UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ 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("E_Commerce.Domain.IdentityModule.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("E_Commerce.Domain.IdentityModule.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("E_Commerce.Domain.IdentityModule.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("E_Commerce.Domain.IdentityModule.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.ApplicationUser", b =>
+ {
+ b.Navigation("Address");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/E-Commerce.Persistance/IdentityData/Migrations/20251120174639_IdentityTablesInitialCreate.cs b/E-Commerce.Persistance/IdentityData/Migrations/20251120174639_IdentityTablesInitialCreate.cs
new file mode 100644
index 0000000..e03b1a6
--- /dev/null
+++ b/E-Commerce.Persistance/IdentityData/Migrations/20251120174639_IdentityTablesInitialCreate.cs
@@ -0,0 +1,258 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace E_Commerce.Persistance.IdentityData.Migrations
+{
+ ///
+ public partial class IdentityTablesInitialCreate : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Roles",
+ columns: table => new
+ {
+ Id = table.Column(type: "nvarchar(450)", nullable: false),
+ Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
+ NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
+ ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Roles", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Users",
+ columns: table => new
+ {
+ Id = table.Column(type: "nvarchar(450)", nullable: false),
+ DisplayName = table.Column(type: "nvarchar(max)", nullable: false),
+ UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
+ NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
+ Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
+ NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
+ EmailConfirmed = table.Column(type: "bit", nullable: false),
+ PasswordHash = table.Column(type: "nvarchar(max)", nullable: true),
+ SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true),
+ ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true),
+ PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true),
+ PhoneNumberConfirmed = table.Column(type: "bit", nullable: false),
+ TwoFactorEnabled = table.Column(type: "bit", nullable: false),
+ LockoutEnd = table.Column(type: "datetimeoffset", nullable: true),
+ LockoutEnabled = table.Column(type: "bit", nullable: false),
+ AccessFailedCount = table.Column(type: "int", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Users", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetRoleClaims",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("SqlServer:Identity", "1, 1"),
+ RoleId = table.Column(type: "nvarchar(450)", nullable: false),
+ ClaimType = table.Column(type: "nvarchar(max)", nullable: true),
+ ClaimValue = table.Column(type: "nvarchar(max)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
+ table.ForeignKey(
+ name: "FK_AspNetRoleClaims_Roles_RoleId",
+ column: x => x.RoleId,
+ principalTable: "Roles",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Addresses",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("SqlServer:Identity", "1, 1"),
+ City = table.Column(type: "nvarchar(max)", nullable: false),
+ Street = table.Column(type: "nvarchar(max)", nullable: false),
+ Country = table.Column(type: "nvarchar(max)", nullable: false),
+ FirstName = table.Column(type: "nvarchar(max)", nullable: false),
+ LastName = table.Column(type: "nvarchar(max)", nullable: false),
+ UserId = table.Column(type: "nvarchar(450)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Addresses", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Addresses_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserClaims",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("SqlServer:Identity", "1, 1"),
+ UserId = table.Column(type: "nvarchar(450)", nullable: false),
+ ClaimType = table.Column(type: "nvarchar(max)", nullable: true),
+ ClaimValue = table.Column(type: "nvarchar(max)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
+ table.ForeignKey(
+ name: "FK_AspNetUserClaims_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserLogins",
+ columns: table => new
+ {
+ LoginProvider = table.Column(type: "nvarchar(450)", nullable: false),
+ ProviderKey = table.Column(type: "nvarchar(450)", nullable: false),
+ ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true),
+ UserId = table.Column(type: "nvarchar(450)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
+ table.ForeignKey(
+ name: "FK_AspNetUserLogins_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserTokens",
+ columns: table => new
+ {
+ UserId = table.Column(type: "nvarchar(450)", nullable: false),
+ LoginProvider = table.Column(type: "nvarchar(450)", nullable: false),
+ Name = table.Column(type: "nvarchar(450)", nullable: false),
+ Value = table.Column(type: "nvarchar(max)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
+ table.ForeignKey(
+ name: "FK_AspNetUserTokens_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "UserRoles",
+ columns: table => new
+ {
+ UserId = table.Column(type: "nvarchar(450)", nullable: false),
+ RoleId = table.Column(type: "nvarchar(450)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId });
+ table.ForeignKey(
+ name: "FK_UserRoles_Roles_RoleId",
+ column: x => x.RoleId,
+ principalTable: "Roles",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_UserRoles_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Addresses_UserId",
+ table: "Addresses",
+ column: "UserId",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetRoleClaims_RoleId",
+ table: "AspNetRoleClaims",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetUserClaims_UserId",
+ table: "AspNetUserClaims",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetUserLogins_UserId",
+ table: "AspNetUserLogins",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "RoleNameIndex",
+ table: "Roles",
+ column: "NormalizedName",
+ unique: true,
+ filter: "[NormalizedName] IS NOT NULL");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_UserRoles_RoleId",
+ table: "UserRoles",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "EmailIndex",
+ table: "Users",
+ column: "NormalizedEmail");
+
+ migrationBuilder.CreateIndex(
+ name: "UserNameIndex",
+ table: "Users",
+ column: "NormalizedUserName",
+ unique: true,
+ filter: "[NormalizedUserName] IS NOT NULL");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Addresses");
+
+ migrationBuilder.DropTable(
+ name: "AspNetRoleClaims");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserClaims");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserLogins");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserTokens");
+
+ migrationBuilder.DropTable(
+ name: "UserRoles");
+
+ migrationBuilder.DropTable(
+ name: "Roles");
+
+ migrationBuilder.DropTable(
+ name: "Users");
+ }
+ }
+}
diff --git a/E-Commerce.Persistance/IdentityData/Migrations/StoreIdentityDbContextModelSnapshot.cs b/E-Commerce.Persistance/IdentityData/Migrations/StoreIdentityDbContextModelSnapshot.cs
new file mode 100644
index 0000000..29a1dd9
--- /dev/null
+++ b/E-Commerce.Persistance/IdentityData/Migrations/StoreIdentityDbContextModelSnapshot.cs
@@ -0,0 +1,336 @@
+//
+using System;
+using E_Commerce.Persistance.IdentityData.DbContexts;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace E_Commerce.Persistance.IdentityData.Migrations
+{
+ [DbContext(typeof(StoreIdentityDbContext))]
+ partial class StoreIdentityDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.Address", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("City")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Country")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Street")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("Addresses", (string)null);
+ });
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.ApplicationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("bit");
+
+ 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("Users", (string)null);
+ });
+
+ 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("Roles", (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")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ProviderKey")
+ .HasColumnType("nvarchar(450)");
+
+ 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("UserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("LoginProvider")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Name")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Value")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.Address", b =>
+ {
+ b.HasOne("E_Commerce.Domain.IdentityModule.ApplicationUser", "User")
+ .WithOne("Address")
+ .HasForeignKey("E_Commerce.Domain.IdentityModule.Address", "UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ 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("E_Commerce.Domain.IdentityModule.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("E_Commerce.Domain.IdentityModule.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("E_Commerce.Domain.IdentityModule.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("E_Commerce.Domain.IdentityModule.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("E_Commerce.Domain.IdentityModule.ApplicationUser", b =>
+ {
+ b.Navigation("Address");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/E-Commerce.Presentaion/Controllers/ApiBaseController.cs b/E-Commerce.Presentaion/Controllers/ApiBaseController.cs
new file mode 100644
index 0000000..c3acacb
--- /dev/null
+++ b/E-Commerce.Presentaion/Controllers/ApiBaseController.cs
@@ -0,0 +1,80 @@
+using E_Commerce.Shared.CommonResult;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Presentaion.Controllers
+{
+ [ApiController]
+ [Route("api/[controller]")]
+ public class ApiBaseController : ControllerBase
+ {
+ protected IActionResult HandleResult(Result result)
+ {
+
+ if (result.IsSuccess)
+ return NoContent();
+ else
+ return HandleProblem(result.Errors);
+
+ }
+
+ protected ActionResult HandleResult(Result result)
+ {
+ if (result.IsSuccess)
+ return Ok(result.Value);
+ else
+ return HandleProblem(result.Errors);
+
+ }
+
+ private ActionResult HandleProblem(IReadOnlyList errors)
+ {
+ // if no errors are provided ,return 500 error
+ if (errors.Count == 0)
+ return Problem(statusCode: StatusCodes.Status500InternalServerError, title: "An Unexpected Error");
+
+ // if all errors are validation errors , handle them as validation problem
+ if (errors.All(e => e.Type == ErrorType.Validation))
+ return HandleValidationProblem(errors);
+
+ // if there id only one error , handle it as a single error problem
+ return HandleSingleErrorProblem(errors[0]);
+
+ }
+ private ActionResult HandleSingleErrorProblem(Error error)
+ {
+
+ return Problem(
+ title: error.Code,
+ detail: error.Descreption,
+ type: error.Type.ToString(),
+ statusCode: MapErrorTypeTostatusCode(error.Type)
+ );
+ }
+ private static int MapErrorTypeTostatusCode(ErrorType errorType) => errorType switch
+ {
+ ErrorType.Failure => StatusCodes.Status500InternalServerError,
+ ErrorType.Validation => StatusCodes.Status400BadRequest,
+ ErrorType.InvalidCredentials => StatusCodes.Status400BadRequest,
+ ErrorType.Unauthorized => StatusCodes.Status401Unauthorized,
+ ErrorType.NotFound => StatusCodes.Status404NotFound,
+ ErrorType.Forbidden => StatusCodes.Status403Forbidden,
+ _ => StatusCodes.Status500InternalServerError
+
+ };
+
+ private ActionResult HandleValidationProblem(IReadOnlyList errors)
+ {
+ var modelstate = new ModelStateDictionary();
+ foreach (var error in errors)
+ modelstate.AddModelError(error.Code, error.Descreption);
+ return ValidationProblem(modelstate);
+ }
+ }
+}
diff --git a/E-Commerce.Presentaion/Controllers/AuthenticationController.cs b/E-Commerce.Presentaion/Controllers/AuthenticationController.cs
new file mode 100644
index 0000000..22d3ad7
--- /dev/null
+++ b/E-Commerce.Presentaion/Controllers/AuthenticationController.cs
@@ -0,0 +1,61 @@
+using E_Commerce.Services.Abstraction;
+using E_Commerce.Shared.DTOs.IdentityDTOs;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Presentaion.Controllers
+{
+ public class AuthenticationController : ApiBaseController
+ {
+ private readonly IAuthenticationService _authenticationService;
+
+ public AuthenticationController(IAuthenticationService authenticationService)
+ {
+ this._authenticationService = authenticationService;
+ }
+
+ //Login
+ // Post : BaseUrl/api/Authentication/Login
+ [HttpPost("Login")]
+ public async Task> Login(LoginDTO loginDTO)
+ {
+ var Result = await _authenticationService.LoginAsync(loginDTO);
+ return HandleResult(Result);
+ }
+
+ // Register
+ // Post : BaseUrl/api/Authentication/Register
+ [HttpPost("Register")]
+ public async Task> Register(RegisterDTO registerDTO)
+ {
+ var Result = await _authenticationService.RegisterAsync(registerDTO);
+ return HandleResult(Result);
+ }
+
+
+ // Get : Get/api/Authentication/emailexists
+ [HttpGet("emailExists")]
+ public async Task> CheckEmail(string email)
+ {
+ var Result = await _authenticationService.CheckEmailAsync(email);
+ return Ok(Result);
+ }
+
+ // Get : Get/api/Authentication/CurrentUser
+ [Authorize]
+ [HttpGet("CurrentUser")]
+ public async Task> GetCurrentUser()
+ {
+ var Email = User.FindFirstValue(ClaimValueTypes.Email)!;
+ var Result = await _authenticationService.GetUserByEmailAsync(Email);
+ return HandleResult(Result);
+ }
+
+ }
+}
diff --git a/E-Commerce.Presentaion/Controllers/ProductController.cs b/E-Commerce.Presentaion/Controllers/ProductController.cs
index 9e215e6..92ad1f5 100644
--- a/E-Commerce.Presentaion/Controllers/ProductController.cs
+++ b/E-Commerce.Presentaion/Controllers/ProductController.cs
@@ -2,6 +2,7 @@
using E_Commerce.Services.Abstraction;
using E_Commerce.Shared;
using E_Commerce.Shared.DTOs.ProductDTOs;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
@@ -12,9 +13,8 @@
namespace E_Commerce.Presentaion.Controllers
{
- [ApiController]
- [Route("api/[controller]")]
- public class ProductController :ControllerBase
+
+ public class ProductController :ApiBaseController
{
private readonly IProductService _productService;
@@ -25,6 +25,7 @@ public ProductController(IProductService productService)
//GetAllProducts
//Get : BaseUrl/api/Products
+ [Authorize]
[HttpGet]
[RedisCache]
public async Task>> GetAllProducts([FromQuery] ProductQueryParams queryParams)
@@ -39,9 +40,10 @@ public async Task>> GetAllProducts([Fro
public async Task> GetProduct(int id)
{
//throw new Exception();
- var Product = await _productService.GetProductByIdAsync(id);
+ var Result = await _productService.GetProductByIdAsync(id);
- return Ok(Product);
+ return HandleResult(Result);
+
}
//GetAllProductTypes
diff --git a/E-Commerce.Services.Abstraction/IAuthenticationService.cs b/E-Commerce.Services.Abstraction/IAuthenticationService.cs
new file mode 100644
index 0000000..8be7060
--- /dev/null
+++ b/E-Commerce.Services.Abstraction/IAuthenticationService.cs
@@ -0,0 +1,22 @@
+using E_Commerce.Shared.CommonResult;
+using E_Commerce.Shared.DTOs.IdentityDTOs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Services.Abstraction
+{
+ public interface IAuthenticationService
+ {
+ //Login
+ Task> LoginAsync(LoginDTO loginDTO);
+
+ //Register
+ Task> RegisterAsync(RegisterDTO registerDTO);
+
+ Task CheckEmailAsync(string email);
+ Task> GetUserByEmailAsync(string email);
+ }
+}
diff --git a/E-Commerce.Services.Abstraction/IProductService.cs b/E-Commerce.Services.Abstraction/IProductService.cs
index bdf3ae3..40cbc25 100644
--- a/E-Commerce.Services.Abstraction/IProductService.cs
+++ b/E-Commerce.Services.Abstraction/IProductService.cs
@@ -1,4 +1,5 @@
using E_Commerce.Shared;
+using E_Commerce.Shared.CommonResult;
using E_Commerce.Shared.DTOs.ProductDTOs;
using System;
using System.Collections.Generic;
@@ -14,7 +15,7 @@ public interface IProductService
Task> GetAllProductAsync(ProductQueryParams queryParams);
// Get Product By ID Return ProductDOT
- Task GetProductByIdAsync(int id);
+ Task> GetProductByIdAsync(int id);
// Get All Brands Return IEumerable of BrandDTO
Task> GetAllBransdAsync();
diff --git a/E-Commerce.Services/AuthenticationService.cs b/E-Commerce.Services/AuthenticationService.cs
new file mode 100644
index 0000000..428313c
--- /dev/null
+++ b/E-Commerce.Services/AuthenticationService.cs
@@ -0,0 +1,114 @@
+using E_Commerce.Domain.IdentityModule;
+using E_Commerce.Services.Abstraction;
+using E_Commerce.Shared.CommonResult;
+using E_Commerce.Shared.DTOs.IdentityDTOs;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.Configuration;
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Services
+{
+ public class AuthenticationService : IAuthenticationService
+ {
+ private readonly UserManager _userManager;
+ private readonly IConfiguration _configuration;
+
+ public AuthenticationService(UserManager userManager, IConfiguration configuration)
+ {
+ _userManager = userManager;
+ _configuration = configuration;
+ }
+
+ public async Task CheckEmailAsync(string email)
+ {
+ var User = await _userManager.FindByEmailAsync(email);
+ return User != null;
+ }
+
+ public async Task> GetUserByEmailAsync(string email)
+ {
+ var User = await _userManager.FindByEmailAsync(email);
+ if (User is null)
+ return Error.NotFound("User.NotFound", $"No User With Email {email} Was Found");
+ return new UserDTO(User.Email!, User.DisplayName, await CreateTokenAsync(User));
+ }
+
+ public async Task> LoginAsync(LoginDTO loginDTO)
+ {
+
+ var User = await _userManager.FindByEmailAsync(loginDTO.Email);
+ if (User == null)
+ return Error.InvalidCredentials("User.InvalidCredentials");
+
+ var IsPasswordValid = await _userManager.CheckPasswordAsync(User, loginDTO.Password);
+ if (!IsPasswordValid)
+ return Error.InvalidCredentials("User.InvalidCredentials");
+ var Token = await CreateTokenAsync(User);
+ return new UserDTO(User.Email!, User.DisplayName, Token);
+
+ }
+
+ public async Task> RegisterAsync(RegisterDTO registerDTO)
+ {
+ var User = new ApplicationUser()
+ {
+ Email = registerDTO.Email,
+ DisplayName = registerDTO.DisplayName,
+ PhoneNumber = registerDTO.PhoneNumber,
+ UserName = registerDTO.UserName
+ };
+ var IdentityResult = await _userManager.CreateAsync(User, registerDTO.Password);
+
+ if (IdentityResult.Succeeded)
+ {
+ var Token = await CreateTokenAsync(User);
+ return new UserDTO(User.Email, User.DisplayName, Token);
+ }
+ return IdentityResult.Errors.Select(e => Error.Validation(e.Code, e.Description)).ToList();
+ }
+
+ private async Task CreateTokenAsync(ApplicationUser user)
+ {
+ // To Create Token [Issuer - Audience - Claims - Expires - SigningCredentials]
+
+ var Claims = new List()
+ {
+ new Claim(JwtRegisteredClaimNames.Email, user.Email!),
+ new Claim(JwtRegisteredClaimNames.Name, user.UserName!)
+ };
+
+ var Roles = await _userManager.GetRolesAsync(user);
+ foreach (var role in Roles)
+ {
+ Claims.Add(new Claim(ClaimTypes.Role, role));
+ }
+
+ var SecretKey = _configuration["JWTOptions:SecretKey"];
+
+ var Key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey!));
+ var SigningCredentials = new SigningCredentials(Key, SecurityAlgorithms.HmacSha256);
+
+ var Token = new JwtSecurityToken
+ (
+ issuer: _configuration["JWTOptions:Issuer"],
+ audience: _configuration["JWTOptions:Audience"],
+ expires: DateTime.UtcNow.AddHours(1),
+ claims: Claims,
+ signingCredentials: SigningCredentials
+ );
+
+ return new JwtSecurityTokenHandler().WriteToken(Token);
+
+
+ }
+
+
+ }
+}
diff --git a/E-Commerce.Services/E-Commerce.Services.csproj b/E-Commerce.Services/E-Commerce.Services.csproj
index 826d180..b85673c 100644
--- a/E-Commerce.Services/E-Commerce.Services.csproj
+++ b/E-Commerce.Services/E-Commerce.Services.csproj
@@ -9,6 +9,7 @@
+
diff --git a/E-Commerce.Services/ProductService.cs b/E-Commerce.Services/ProductService.cs
index d7ea8e2..b097ee8 100644
--- a/E-Commerce.Services/ProductService.cs
+++ b/E-Commerce.Services/ProductService.cs
@@ -5,6 +5,7 @@
using E_Commerce.Services.Exceptions;
using E_Commerce.Services.Specifications;
using E_Commerce.Shared;
+using E_Commerce.Shared.CommonResult;
using E_Commerce.Shared.DTOs.ProductDTOs;
using System;
using System.Collections.Generic;
@@ -55,15 +56,15 @@ public async Task> GetAllTypesdAsync()
}
- public async Task GetProductByIdAsync(int id)
+ public async Task> GetProductByIdAsync(int id)
{
var spec = new ProductWithTypeAndBrandSpecification(id);
var Product = await _unitOfWork.GetRepository().GetByIdAsync(spec);
+
+ if (Product is null)
+ return (Error.NotFound("Product.NotFound",$"Product With {id} is Not Found"));
- if (Product == null)
- throw new ProductNotFoundException(id);
-
- return _mapper.Map(Product);
+ return _mapper.Map(Product); // Implicit Casting To Resutl.Fail - Resutl.OK
}
}
}
diff --git a/E-Commerce.Shared/CommonResult/Error.cs b/E-Commerce.Shared/CommonResult/Error.cs
new file mode 100644
index 0000000..319bd27
--- /dev/null
+++ b/E-Commerce.Shared/CommonResult/Error.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Shared.CommonResult
+{
+ public class Error
+ {
+
+ public string Code { get; }
+ public string Descreption { get; }
+ public ErrorType Type { get; }
+ private Error(string code, string descreption, ErrorType type)
+ {
+ Code = code;
+ Descreption = descreption;
+ Type = type;
+ }
+
+ #region Static Factory Methods
+
+ public static Error Failure(string Code = "General.Failure", string Descreption = "An unexpected error occurred. Please try again later")
+ {
+ return new Error(Code, Descreption, ErrorType.Failure);
+ }
+ public static Error Validation(string Code = "General.Validation", string Descreption = "Validation Error Has Occured")
+ {
+ return new Error(Code, Descreption, ErrorType.Validation);
+ }
+ public static Error NotFound(string Code = "General.NotFound", string Descreption = "The Requested Resource Was Not Found")
+ {
+ return new Error(Code, Descreption, ErrorType.NotFound);
+ }
+ public static Error Unauthorized(string Code = "General.Unauthorized", string Descreption = "You Are Not Authorized To This Process")
+ {
+ return new Error(Code, Descreption, ErrorType.Unauthorized);
+ }
+ public static Error Forbidden(string Code = "General.Forbidden", string Descreption = "You Do Not Have Permission To This Process")
+ {
+ return new Error(Code, Descreption, ErrorType.Forbidden);
+ }
+ public static Error InvalidCredentials(string Code = "General.InvalidCredentials", string Descreption = "The provided credentials are incorrect")
+ {
+ return new Error(Code, Descreption, ErrorType.InvalidCredentials);
+ }
+ #endregion
+
+ }
+}
diff --git a/E-Commerce.Shared/CommonResult/ErrorType.cs b/E-Commerce.Shared/CommonResult/ErrorType.cs
new file mode 100644
index 0000000..a20976d
--- /dev/null
+++ b/E-Commerce.Shared/CommonResult/ErrorType.cs
@@ -0,0 +1,12 @@
+namespace E_Commerce.Shared.CommonResult
+{
+ public enum ErrorType
+ {
+ Failure = 0,
+ Validation = 1,
+ NotFound = 2,
+ Unauthorized = 3,
+ Forbidden = 4,
+ InvalidCredentials = 5
+ }
+}
\ No newline at end of file
diff --git a/E-Commerce.Shared/CommonResult/Result.cs b/E-Commerce.Shared/CommonResult/Result.cs
new file mode 100644
index 0000000..0e186d9
--- /dev/null
+++ b/E-Commerce.Shared/CommonResult/Result.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace E_Commerce.Shared.CommonResult
+{
+ public class Result
+ {
+ private readonly List _errors = [];
+ public IReadOnlyList Errors => _errors;
+ public bool IsSuccess => _errors.Count == 0; // true
+ public bool IsFailure => !IsSuccess; // false
+
+ //Ok - Success
+ protected Result() { }
+ //Fail With Error
+ public Result(Error error)
+ {
+ _errors.Add(error);
+ }
+ //Fail With List
+ public Result(List errors)
+ {
+ _errors = errors;
+ }
+
+
+
+
+
+
+ }
+
+ public class Result : Result
+ {
+ private readonly TValue _value;
+ public TValue Value => IsSuccess ? _value : throw new InvalidOperationException("Can Not Access To The Value");
+
+ //Ok - Success With Value
+ protected Result(TValue value) : base()
+ {
+ _value = value;
+ }
+
+ //Fail With Error With Value
+ public Result(Error error) : base()
+ {
+ _value = default!;
+ }
+
+ //Fail With List With Value
+ public Result(List errors) : base()
+ {
+ _value = default!;
+
+ }
+
+ public static Result OK(TValue value) => new Result(value);
+ public static Result Fail(Error error) => new Result(error);
+ public static Result Fail(List errors) => new Result(errors);
+
+
+ public static implicit operator Result(TValue value) => OK(value);
+ public static implicit operator Result(Error error) => Fail(error);
+ public static implicit operator Result(List errors) => Fail(errors);
+
+
+ }
+}
diff --git a/E-Commerce.Shared/DTOs/IdentityDTOs/LoginDTO.cs b/E-Commerce.Shared/DTOs/IdentityDTOs/LoginDTO.cs
new file mode 100644
index 0000000..f63fd6c
--- /dev/null
+++ b/E-Commerce.Shared/DTOs/IdentityDTOs/LoginDTO.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Shared.DTOs.IdentityDTOs
+{
+ public record LoginDTO([EmailAddress]string Email, string Password);
+
+}
diff --git a/E-Commerce.Shared/DTOs/IdentityDTOs/RegisterDTO.cs b/E-Commerce.Shared/DTOs/IdentityDTOs/RegisterDTO.cs
new file mode 100644
index 0000000..6e0b2a3
--- /dev/null
+++ b/E-Commerce.Shared/DTOs/IdentityDTOs/RegisterDTO.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Shared.DTOs.IdentityDTOs
+{
+ public record RegisterDTO([EmailAddress] string Email, string DisplayName,string UserName,string Password,[Phone]string PhoneNumber);
+
+}
diff --git a/E-Commerce.Shared/DTOs/IdentityDTOs/UserDTO.cs b/E-Commerce.Shared/DTOs/IdentityDTOs/UserDTO.cs
new file mode 100644
index 0000000..80ce750
--- /dev/null
+++ b/E-Commerce.Shared/DTOs/IdentityDTOs/UserDTO.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace E_Commerce.Shared.DTOs.IdentityDTOs
+{
+ public record UserDTO(string Email,string DisplayName,string Token);
+
+
+
+}
diff --git a/E-CommerceProject/CustomMiddleWares/ExceptionHandlerMiddleWare.cs b/E-CommerceProject/CustomMiddleWares/ExceptionHandlerMiddleWare.cs
index f334e4e..006068c 100644
--- a/E-CommerceProject/CustomMiddleWares/ExceptionHandlerMiddleWare.cs
+++ b/E-CommerceProject/CustomMiddleWares/ExceptionHandlerMiddleWare.cs
@@ -1,4 +1,5 @@
-using E_Commerce.Services.Exceptions;
+using Azure;
+using E_Commerce.Services.Exceptions;
using Microsoft.AspNetCore.Mvc;
namespace E_CommerceProject.CustomMiddleWares
@@ -48,16 +49,16 @@ public async Task Invoke(HttpContext httpcontext)
private static async Task HandleNotFoundEndPointAsync(HttpContext httpcontext)
{
- if (httpcontext.Response.StatusCode == StatusCodes.Status404NotFound)
+ if (httpcontext.Response.StatusCode == StatusCodes.Status404NotFound && !httpcontext.Response.HasStarted)
{
- var Problem = new ProblemDetails()
+ var Response = new ProblemDetails()
{
Title = "Error Will Processing The Http Request - EndPoint Not Found !",
Status = StatusCodes.Status404NotFound,
Detail = $"EndPoint {httpcontext.Request.Path} Not Found",
Instance = httpcontext.Request.Path
};
- await httpcontext.Response.WriteAsJsonAsync(Problem);
+ await httpcontext.Response.WriteAsJsonAsync(Response);
}
}
}
diff --git a/E-CommerceProject/Extentions/WebApplicationRegistration.cs b/E-CommerceProject/Extentions/WebApplicationRegistration.cs
index b00212f..0586df2 100644
--- a/E-CommerceProject/Extentions/WebApplicationRegistration.cs
+++ b/E-CommerceProject/Extentions/WebApplicationRegistration.cs
@@ -1,5 +1,6 @@
using E_Commerce.Domain.Contracts;
using E_Commerce.Persistance.Data.Contexts;
+using E_Commerce.Persistance.IdentityData.DbContexts;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
@@ -8,6 +9,15 @@ namespace E_CommerceProject.Extentions
public static class WebApplicationRegistration
{
public static async Task MigrateDatabaseAsync(this WebApplication app)
+ {
+ await using var scope = app.Services.CreateAsyncScope();
+ var dbContextService = scope.ServiceProvider.GetRequiredService();
+ var PendingMigtations = await dbContextService.Database.GetPendingMigrationsAsync();
+ if (PendingMigtations.Any())
+ await dbContextService.Database.MigrateAsync();
+ return app;
+ }
+ public static async Task MigrateIdentityDatabaseAsync(this WebApplication app)
{
await using var scope = app.Services.CreateAsyncScope();
var dbContextService = scope.ServiceProvider.GetRequiredService();
@@ -21,11 +31,21 @@ public static async Task SeedDatabaseAsync(this WebApplication a
{
await using var scope = app.Services.CreateAsyncScope();
- var DataInitializerService = scope.ServiceProvider.GetRequiredService();
+ var DataInitializerService = scope.ServiceProvider.GetRequiredKeyedService("Default");
await DataInitializerService.InitializeAsync();
return app;
}
+ public static async Task SeedIdentityDatabaseAsync(this WebApplication app)
+ {
+ await using var scope = app.Services.CreateAsyncScope();
+
+ var DataInitializerService = scope.ServiceProvider.GetRequiredKeyedService("Identity");
+ await DataInitializerService.InitializeAsync();
+
+ return app;
+ }
+
}
}
diff --git a/E-CommerceProject/Program.cs b/E-CommerceProject/Program.cs
index 68e0784..9442b9a 100644
--- a/E-CommerceProject/Program.cs
+++ b/E-CommerceProject/Program.cs
@@ -1,7 +1,10 @@
using E_Commerce.Domain.Contracts;
+using E_Commerce.Domain.IdentityModule;
using E_Commerce.Persistance.Data.Contexts;
using E_Commerce.Persistance.Data.DataSeed;
+using E_Commerce.Persistance.IdentityData.DbContexts;
+using E_Commerce.Persistance.IdentityData.IdentityData;
using E_Commerce.Persistance.Repositories;
using E_Commerce.Services;
using E_Commerce.Services.Abstraction;
@@ -9,10 +12,14 @@
using E_CommerceProject.CustomMiddleWares;
using E_CommerceProject.Extentions;
using E_CommerceProject.Factories;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
+using Microsoft.IdentityModel.Tokens;
using StackExchange.Redis;
using System.Reflection;
+using System.Text;
using System.Threading.Tasks;
namespace E_CommerceProject
@@ -33,7 +40,9 @@ public static async Task Main(string[] args)
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
- builder.Services.AddScoped();
+ builder.Services.AddKeyedScoped("Default");
+ builder.Services.AddKeyedScoped("Identity");
+
builder.Services.AddScoped();
//builder.Services.AddAutoMapper(x=>x.AddProfile());
@@ -57,6 +66,43 @@ public static async Task Main(string[] args)
options.InvalidModelStateResponseFactory = ApiResponseFactory.GenerateApiValidationResponse;
});
+
+ builder.Services.AddDbContext(options =>
+ {
+
+ options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection"));
+
+ });
+
+ builder.Services.AddIdentityCore()
+ .AddRoles()
+ .AddEntityFrameworkStores();
+ builder.Services.AddScoped();
+
+ builder.Services.AddAuthentication(Options =>
+ {
+ Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ }).AddJwtBearer(Options =>
+ {
+
+ Options.SaveToken = true;
+ Options.TokenValidationParameters = new TokenValidationParameters()
+ {
+
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidIssuer = builder.Configuration["JWTOptions:Issuer"],
+ ValidAudience = builder.Configuration["JWTOptions:Audience"],
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWTOptions:SecretKey"]))
+
+ };
+
+ });
+
+
+
#endregion
#region Redis Connection
@@ -67,30 +113,15 @@ public static async Task Main(string[] args)
#region Data Seeding - Pending Migations
await app.MigrateDatabaseAsync(); // Method In Extention Folder In E-Commerce.Web
+ await app.MigrateIdentityDatabaseAsync(); // Method In Extention Folder In E-Commerce.Web
await app.SeedDatabaseAsync(); // Method In Extention Folder In E-Commerce.Web
+ await app.SeedIdentityDatabaseAsync(); // Method In Extention Folder In E-Commerce.Web
#endregion
#region Configure the HTTP request pipeline.
- //app.Use(async (Context, Next) =>
- //{
- // try
- // {
- // await Next.Invoke(Context);
-
- // }
- // catch (Exception ex)
- // {
- // Console.WriteLine(ex.Message);
- // Context.Response.StatusCode = StatusCodes.Status500InternalServerError;
- // await Context.Response.WriteAsJsonAsync(new
- // {
- // StatusCode = StatusCodes.Status500InternalServerError,
- // Error = $" An Unexpected Error Occurred : {ex.Message}"
- // });
- // }
- //});
+
app.UseMiddleware();
@@ -103,7 +134,7 @@ public static async Task Main(string[] args)
app.UseHttpsRedirection();
app.UseStaticFiles();
-
+ app.UseAuthentication();
app.UseAuthorization();
diff --git a/E-CommerceProject/appsettings.Development.json b/E-CommerceProject/appsettings.Development.json
index 7da8afb..5628a6d 100644
--- a/E-CommerceProject/appsettings.Development.json
+++ b/E-CommerceProject/appsettings.Development.json
@@ -7,9 +7,8 @@
},
"ConnectionStrings": {
"DefaultConnection": " Server =.; Database =Ecommerce; Trusted_Connection=True;TrustServerCertificate=True;",
- "RedisConnection": "localhost"
- //"RedisConnection": "127.0.0.1:6379"
-
+ "RedisConnection": "localhost",
+ "IdentityConnection": " Server =.; Database =Ecommerce.Identity; Trusted_Connection=True;TrustServerCertificate=True;"
@@ -17,6 +16,11 @@
},
"URLs": {
"BaseUrl": "https://localhost:7195/"
+ },
+ "JWTOptions": {
+ "SecretKey": "47735f00f7edd4401b2c1b7fa358007a",
+ "Issuer": "https://localhost:7195/",
+ "Audience": "https://localhost:7195/"
}
}