Skip to content

Commit

Permalink
Refactor template to move DataModels to ServiceInterface + use Custom…
Browse files Browse the repository at this point in the history
…UserSession
  • Loading branch information
mythz committed Nov 16, 2023
1 parent 0e5fc64 commit 828203a
Show file tree
Hide file tree
Showing 13 changed files with 78 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace MyApp.Data;

public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : IdentityDbContext<ApplicationUser>(options)
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<ApplicationUser>(options)
{
}
File renamed without changes.
42 changes: 42 additions & 0 deletions MyApp.ServiceInterface/Data/CustomUserSession.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}

/// <summary>
/// Add additional claims to the Identity Auth Cookie
/// </summary>
public class AdditionalUserClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: UserClaimsPrincipalFactory<ApplicationUser,IdentityRole>(userManager, roleManager, optionsAccessor)
{
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity!;

var claims = new List<Claim>();
// Add additional claims here
if (user.ProfileUrl != null)
{
claims.Add(new Claim(JwtClaimTypes.Picture, user.ProfileUrl));
}

identity.AddClaims(claims);
return principal;
}
}
2 changes: 2 additions & 0 deletions MyApp.ServiceInterface/MyApp.ServiceInterface.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.*" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.*" />
<PackageReference Include="ServiceStack" Version="8.*" />
<PackageReference Include="ServiceStack.Ormlite" Version="8.*" />
</ItemGroup>
Expand Down
13 changes: 1 addition & 12 deletions MyApp/Configure.AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
}
1 change: 1 addition & 0 deletions MyApp/Configure.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public void Configure(IWebHostBuilder builder) => builder
{
appHost.Plugins.Add(new AuthFeature(IdentityAuth.For<ApplicationUser>(options => {
options.EnableCredentialsAuth = true;
options.SessionFactory = () => new CustomUserSession();
})));
});
}
3 changes: 3 additions & 0 deletions MyApp/Configure.Db.Migrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
10 changes: 2 additions & 8 deletions MyApp/Configure.Mq.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,11 @@ public void Configure(IWebHostBuilder builder) => builder
/// <summary>
/// Sends emails by publishing a message to the Background MQ Server where it's processed in the background
/// </summary>
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,
Expand Down
40 changes: 22 additions & 18 deletions MyApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<UserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
services.AddCascadingAuthenticationState();
services.AddScoped<UserAccessor>();
services.AddScoped<IdentityRedirectManager>();
services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();

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<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var connectionString = config.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString, b => b.MigrationsAssembly(nameof(MyApp))));
services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();

builder.Services.AddSingleton<IEmailSender, NoOpEmailSender>();
services.AddSingleton<IEmailSender, NoOpEmailSender>();
// Uncomment to send emails with SMTP, configure SMTP with "SmtpConfig" in appsettings.json
// builder.Services.AddSingleton<IEmailSender, EmailSender>();
// services.AddSingleton<IEmailSender, EmailSender>();
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, 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();

Expand Down
6 changes: 3 additions & 3 deletions MyApp/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"Name": "Lucy Bates",
"Email": "[email protected]",
"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"
Expand All @@ -38,15 +38,15 @@
"Name": "Gayle Smith",
"Email": "[email protected]",
"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"
},
{
"Name": "Brandon Foley",
"Email": "[email protected]",
"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"
}
]
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes

0 comments on commit 828203a

Please sign in to comment.