From 828203a2685a66d079cf2fde10afa38e5bebaf22 Mon Sep 17 00:00:00 2001 From: Demis Bellot Date: Fri, 17 Nov 2023 01:34:47 +0800 Subject: [PATCH] Refactor template to move DataModels to ServiceInterface + use CustomUserSession --- .../Data/ApplicationDbContext.cs | 3 +- .../Data/ApplicationUser.cs | 0 .../Data/CustomUserSession.cs | 42 +++++++++++++++++++ .../MyApp.ServiceInterface.csproj | 2 + MyApp/Configure.AppHost.cs | 13 +----- MyApp/Configure.Auth.cs | 1 + MyApp/Configure.Db.Migrations.cs | 3 ++ MyApp/Configure.Mq.cs | 10 +---- MyApp/Program.cs | 40 ++++++++++-------- MyApp/appsettings.json | 6 +-- .../author1.svg => profiles/user1.svg} | 30 ------------- .../author2.svg => profiles/user2.svg} | 0 .../author3.svg => profiles/user3.svg} | 0 13 files changed, 78 insertions(+), 72 deletions(-) rename {MyApp => MyApp.ServiceInterface}/Data/ApplicationDbContext.cs (72%) rename {MyApp => MyApp.ServiceInterface}/Data/ApplicationUser.cs (100%) create mode 100644 MyApp.ServiceInterface/Data/CustomUserSession.cs rename MyApp/wwwroot/img/{authors/author1.svg => profiles/user1.svg} (97%) rename MyApp/wwwroot/img/{authors/author2.svg => profiles/user2.svg} (100%) rename MyApp/wwwroot/img/{authors/author3.svg => profiles/user3.svg} (100%) diff --git a/MyApp/Data/ApplicationDbContext.cs b/MyApp.ServiceInterface/Data/ApplicationDbContext.cs similarity index 72% rename from MyApp/Data/ApplicationDbContext.cs rename to MyApp.ServiceInterface/Data/ApplicationDbContext.cs index ae92575..fca17f3 100644 --- a/MyApp/Data/ApplicationDbContext.cs +++ b/MyApp.ServiceInterface/Data/ApplicationDbContext.cs @@ -3,6 +3,7 @@ namespace MyApp.Data; -public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) +public class ApplicationDbContext(DbContextOptions options) + : IdentityDbContext(options) { } diff --git a/MyApp/Data/ApplicationUser.cs b/MyApp.ServiceInterface/Data/ApplicationUser.cs similarity index 100% rename from MyApp/Data/ApplicationUser.cs rename to MyApp.ServiceInterface/Data/ApplicationUser.cs diff --git a/MyApp.ServiceInterface/Data/CustomUserSession.cs b/MyApp.ServiceInterface/Data/CustomUserSession.cs new file mode 100644 index 0000000..5c6be87 --- /dev/null +++ b/MyApp.ServiceInterface/Data/CustomUserSession.cs @@ -0,0 +1,42 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using ServiceStack; +using ServiceStack.Web; + +namespace MyApp.Data; + +public class CustomUserSession : AuthUserSession +{ + public override void PopulateFromClaims(IRequest httpReq, ClaimsPrincipal principal) + { + // Populate Session with data from Identity Auth Claims + ProfileUrl = principal.FindFirstValue(JwtClaimTypes.Picture); + } +} + +/// +/// Add additional claims to the Identity Auth Cookie +/// +public class AdditionalUserClaimsPrincipalFactory( + UserManager userManager, + RoleManager roleManager, + IOptions optionsAccessor) + : UserClaimsPrincipalFactory(userManager, roleManager, optionsAccessor) +{ + public override async Task CreateAsync(ApplicationUser user) + { + var principal = await base.CreateAsync(user); + var identity = (ClaimsIdentity)principal.Identity!; + + var claims = new List(); + // Add additional claims here + if (user.ProfileUrl != null) + { + claims.Add(new Claim(JwtClaimTypes.Picture, user.ProfileUrl)); + } + + identity.AddClaims(claims); + return principal; + } +} diff --git a/MyApp.ServiceInterface/MyApp.ServiceInterface.csproj b/MyApp.ServiceInterface/MyApp.ServiceInterface.csproj index 9a854c5..85357f8 100644 --- a/MyApp.ServiceInterface/MyApp.ServiceInterface.csproj +++ b/MyApp.ServiceInterface/MyApp.ServiceInterface.csproj @@ -7,6 +7,8 @@ + + diff --git a/MyApp/Configure.AppHost.cs b/MyApp/Configure.AppHost.cs index 1cf3b20..915e707 100644 --- a/MyApp/Configure.AppHost.cs +++ b/MyApp/Configure.AppHost.cs @@ -25,18 +25,7 @@ public AppHost() : base("MyApp", typeof(MyServices).Assembly) { } // Configure your AppHost with the necessary configuration and dependencies your App needs public override void Configure(Container container) { - SetConfig(new HostConfig - { - AdminAuthSecret = "secretz", + SetConfig(new HostConfig { }); - - Plugins.Add(new CorsFeature(new[] { - "http://localhost:5173", //vite dev - }, allowCredentials: true)); } } - -// Add any additional metadata properties you want to store in the Users Typed Session -public class CustomUserSession : AuthUserSession -{ -} \ No newline at end of file diff --git a/MyApp/Configure.Auth.cs b/MyApp/Configure.Auth.cs index ae482d7..c913e11 100644 --- a/MyApp/Configure.Auth.cs +++ b/MyApp/Configure.Auth.cs @@ -12,6 +12,7 @@ public void Configure(IWebHostBuilder builder) => builder { appHost.Plugins.Add(new AuthFeature(IdentityAuth.For(options => { options.EnableCredentialsAuth = true; + options.SessionFactory = () => new CustomUserSession(); }))); }); } diff --git a/MyApp/Configure.Db.Migrations.cs b/MyApp/Configure.Db.Migrations.cs index 3a1734e..fd0ff41 100644 --- a/MyApp/Configure.Db.Migrations.cs +++ b/MyApp/Configure.Db.Migrations.cs @@ -88,6 +88,7 @@ await EnsureUserAsync(new ApplicationUser FirstName = "Test", LastName = "User", EmailConfirmed = true, + ProfileUrl = "/img/profiles/user1.svg", }, "p@55wOrd"); await EnsureUserAsync(new ApplicationUser @@ -98,6 +99,7 @@ await EnsureUserAsync(new ApplicationUser FirstName = "Test", LastName = "Employee", EmailConfirmed = true, + ProfileUrl = "/img/profiles/user2.svg", }, "p@55wOrd", [Roles.Employee]); await EnsureUserAsync(new ApplicationUser @@ -108,6 +110,7 @@ await EnsureUserAsync(new ApplicationUser FirstName = "Test", LastName = "Manager", EmailConfirmed = true, + ProfileUrl = "/img/profiles/user3.svg", }, "p@55wOrd", [Roles.Manager, Roles.Employee]); await EnsureUserAsync(new ApplicationUser diff --git a/MyApp/Configure.Mq.cs b/MyApp/Configure.Mq.cs index 3d6f465..b00855b 100644 --- a/MyApp/Configure.Mq.cs +++ b/MyApp/Configure.Mq.cs @@ -34,17 +34,11 @@ public void Configure(IWebHostBuilder builder) => builder /// /// Sends emails by publishing a message to the Background MQ Server where it's processed in the background /// -public class EmailSender : IEmailSender +public class EmailSender(IMessageService messageService) : IEmailSender { - IMessageService MessageService { get; } - public EmailSender(IMessageService messageService) - { - MessageService = messageService; - } - public Task SendEmailAsync(string email, string subject, string htmlMessage) { - using var mqClient = MessageService.CreateMessageProducer(); + using var mqClient = messageService.CreateMessageProducer(); mqClient.Publish(new SendEmail { To = email, diff --git a/MyApp/Program.cs b/MyApp/Program.cs index 39f3056..e8bca67 100644 --- a/MyApp/Program.cs +++ b/MyApp/Program.cs @@ -12,46 +12,50 @@ var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; +var config = builder.Configuration; + // Add services to the container. -builder.Services.AddRazorComponents(); +services.AddRazorComponents(); -builder.Services.AddCascadingAuthenticationState(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); +services.AddCascadingAuthenticationState(); +services.AddScoped(); +services.AddScoped(); +services.AddScoped(); -builder.Services.AddAuthorization(); -builder.Services.AddAuthentication(options => +services.AddAuthorization(); +services.AddAuthentication(options => { options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; }) .AddIdentityCookies(); -builder.Services.AddDataProtection() +services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo("App_Data")); // $ dotnet ef migrations add CreateIdentitySchema // $ dotnet ef database update -var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); -builder.Services.AddDbContext(options => - options.UseSqlite(connectionString)); -builder.Services.AddDatabaseDeveloperPageExceptionFilter(); +var connectionString = config.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); +services.AddDbContext(options => + options.UseSqlite(connectionString, b => b.MigrationsAssembly(nameof(MyApp)))); +services.AddDatabaseDeveloperPageExceptionFilter(); -builder.Services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) +services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) .AddRoles() .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultTokenProviders(); -builder.Services.AddSingleton(); +services.AddSingleton(); // Uncomment to send emails with SMTP, configure SMTP with "SmtpConfig" in appsettings.json -// builder.Services.AddSingleton(); +// services.AddSingleton(); +services.AddScoped, AdditionalUserClaimsPrincipalFactory>(); var baseUrl = builder.Configuration["ApiBaseUrl"] ?? (builder.Environment.IsDevelopment() ? "https://localhost:5001" : "http://" + IPAddress.Loopback); -builder.Services.AddScoped(c => new HttpClient { BaseAddress = new Uri(baseUrl) }); -builder.Services.AddBlazorServerIdentityApiClient(baseUrl); -builder.Services.AddLocalStorage(); +services.AddScoped(c => new HttpClient { BaseAddress = new Uri(baseUrl) }); +services.AddBlazorServerIdentityApiClient(baseUrl); +services.AddLocalStorage(); var app = builder.Build(); diff --git a/MyApp/appsettings.json b/MyApp/appsettings.json index 56105b1..4b0327a 100644 --- a/MyApp/appsettings.json +++ b/MyApp/appsettings.json @@ -29,7 +29,7 @@ "Name": "Lucy Bates", "Email": "lucy@email.org", "Bio": "Works in software design, company building, and the aerospace industry.", - "ProfileUrl": "img/authors/author1.svg", + "ProfileUrl": "img/profiles/user1.svg", "TwitterUrl": "https://twitter.com/lucy", "ThreadsUrl": "https://threads.net/@lucy", "GitHubUrl": "https://github.com/lucy" @@ -38,7 +38,7 @@ "Name": "Gayle Smith", "Email": "gayle@email.org", "Bio": "Gayle is an author, consultant, and founder focusing on improving tech.", - "ProfileUrl": "img/authors/author2.svg", + "ProfileUrl": "img/profiles/user2.svg", "TwitterUrl": "https://twitter.com/gayle", "MastodonUrl": "https://mastodon.social/@gayle" }, @@ -46,7 +46,7 @@ "Name": "Brandon Foley", "Email": "brandon@email.org", "Bio": "I am a programmer at heart. I like to tinker with code and build generic coding structures.", - "ProfileUrl": "img/authors/author3.svg", + "ProfileUrl": "img/profiles/user3.svg", "GitHubUrl": "https://github.com/brandon" } ] diff --git a/MyApp/wwwroot/img/authors/author1.svg b/MyApp/wwwroot/img/profiles/user1.svg similarity index 97% rename from MyApp/wwwroot/img/authors/author1.svg rename to MyApp/wwwroot/img/profiles/user1.svg index f980887..974090a 100644 --- a/MyApp/wwwroot/img/authors/author1.svg +++ b/MyApp/wwwroot/img/profiles/user1.svg @@ -52,34 +52,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MyApp/wwwroot/img/authors/author2.svg b/MyApp/wwwroot/img/profiles/user2.svg similarity index 100% rename from MyApp/wwwroot/img/authors/author2.svg rename to MyApp/wwwroot/img/profiles/user2.svg diff --git a/MyApp/wwwroot/img/authors/author3.svg b/MyApp/wwwroot/img/profiles/user3.svg similarity index 100% rename from MyApp/wwwroot/img/authors/author3.svg rename to MyApp/wwwroot/img/profiles/user3.svg