How to integrated MS DefaulUI with CustomUserStore
This long way started from my database, I have very simple database in MySQL and don't want follow idiotic Microsoft rules to implement Microsoft story. Microsoft way is most idiotic way as possible in any case:
- I don't want use MS SQL server, I want to use MySQL.
- I don't want use View and controller code in one project template Split code and view to different projects with ASP.NET Core 6.
- I don't want use dumb Microsoft DataStory - PasswordSalt and other trash fields in user definition with serious project. I leave this future to mindless programmers always follow idiotic Microsoft rules.
- I don't want to use dumb C# language in controller code. Why Microsoft vilified Visual Basic ?.
- I don't want to use idiotic workflow named Code First, I want to use classic developer workflow Database first. I want to develop software by classic way from database instead opposite to human logic Microsoft way from ass to head.
- This is example of my DB and I want to explain my DB without follow my own visible to world, not for idiotic view of idiotic programmers hired to Microsoft.
But I want to use in my project standard View and algorithm from hidden area Identity. With my own database in MySQL server!
What child hired from Microsoft propose for public?
- Create project with support individual account.
- We receive project with Default Identity. Of course, MS documentation has no any useful information how to avoid default identity.
Project contains idiotic project template (mix View and Code on one project), standard Login/Register workflow and definition of idiotic database with idiotic proprietary server.
- Than we need to deploy DB and connect DB to project.
- All this trash is not useful, this trash is treasure only low level C# newbie, not for experienced VB programmer. I use from this trash only Identity workflow.
So, we need realize UserManager and SignManager without idiotic template EF Code first, without deploying from code to DB (we going from existing DB to code), with free MySQL server without proprietary MsSQL. We don't use C# in project code, we don't use idiotic MS template with mix code and View in one project. And we don't use idiotic MS Database structure needed to UserManager and SignManager.
This allow me embed standard Login/Registration logic to my project.
So, firstly we need to configure Net core services.
But firstly look details to project explorer.
- FrontEndRoot - this is folder only. I build special extension for VS2022 to use this function https://marketplace.visualstudio.com/items?itemName=alexev14.AddFolderToSolutionForVS2022
- FrontEndForm - this is project with View only, Main project FrontEndCode has link only to project with View.
- Data - is project with common library of my project. And this is solution structure on the disk.
So, this is DI-container configuration of main project FrontEndCode.
1: Imports BackendAPI
2: Imports BackendAPI.Model
3: Imports BackendAPI.Notification
4: Imports BackendAPI.Services
5: Imports FrontEnd.Data
6: Imports FrontEndData
7: Imports FrontEndData.Models
8: Imports Microsoft.AspNetCore.Authentication.Cookies
9: Imports Microsoft.AspNetCore.Builder
10: Imports Microsoft.AspNetCore.Hosting
11: Imports Microsoft.AspNetCore.Http
12: Imports Microsoft.AspNetCore.Http.Connections
13: Imports Microsoft.AspNetCore.HttpOverrides
14: Imports Microsoft.AspNetCore.Identity
15: Imports Microsoft.AspNetCore.Mvc.ApplicationModels
16: Imports Microsoft.AspNetCore.Mvc.RazorPages
17: Imports Microsoft.AspNetCore.Routing
18: Imports Microsoft.EntityFrameworkCore
19: Imports Microsoft.Extensions.Configuration
20: Imports Microsoft.Extensions.DependencyInjection
21: Imports Microsoft.Extensions.FileProviders
22: Imports Microsoft.Extensions.Hosting
23: Imports Microsoft.Extensions.Logging
24: Imports Microsoft.OpenApi.Models
25: Imports System.Diagnostics
26: Imports Microsoft.AspNetCore.Mvc
27: Imports Microsoft.AspNetCore
28: Imports Microsoft.Extensions.Options
29: Imports Newtonsoft.Json
30: Imports BackendAPI.FrontEndModels
31: Imports Microsoft.AspNetCore.Mvc.Infrastructure
32: Imports Microsoft.AspNetCore.StaticFiles
33:
34: Module Program
35:
36: Public Property Environment As IWebHostEnvironment
37: Public Property LoggerFactory As ILoggerFactory
38: Public Property Configuration As IConfiguration
39: Sub Main(Args() As String)
40: Dim Builder = WebApplication.CreateBuilder(Args)
41: 'Add services to the container.
42: Debug.WriteLine($"ContentRootPath: {Builder.Environment.ContentRootPath}, WebRootPath: {Builder.Environment.WebRootPath}, StaticFilesRoot: {Builder.Configuration("StaticFilesRoot")}, RazorPagesRoot: ?, WebRootFileProvider: {Builder.Environment.WebRootFileProvider}, IsDevelopment: {Builder.Environment.IsDevelopment} ")
43:
44: Builder.Host.ConfigureLogging(Sub(hostingContext, logging)
45: logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"))
46: logging.AddConsole
47: logging.AddDebug
48: 'logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Debug)
49: logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Debug)
50: End Sub)
51:
52: Builder.WebHost.ConfigureKestrel(Sub(KestrelServerOptions)
53: KestrelServerOptions.ListenLocalhost(5158)
54: KestrelServerOptions.ListenAnyIP(7168, Function(X) X.UseHttps)
55: End Sub)
56: Environment = Builder.Environment
57: Configuration = Builder.Configuration
58:
59: Builder.Services.AddHttpContextAccessor
60: Builder.Services.AddSingleton(Of IActionContextAccessor, ActionContextAccessor)
61:
62: 'The call to AddMvc also calls AddRazorPages internally
63: Builder.Services.AddMvcCore(Sub(MvcOptions) MvcOptions.EnableEndpointRouting = False)
64:
65: Dim AES As New AesCryptor
66:
67: Builder.Services.AddDbContext(Of ApplicationDbContext)(Function(ByVal options As DbContextOptionsBuilder)
68: Return options.UseMySql(AES.DecryptSqlConnection(Builder.Configuration.GetConnectionString("DefaultConnection"), "XXXXXXXXXXXXXXXXX"),
69: ServerVersion.Parse("10.5.9-MariaDB-1:10.5.9+maria~xenial"), 'SHOW VARIABLES LIKE "%version%";
70: Sub(ByVal mySqlOption As Microsoft.EntityFrameworkCore.Infrastructure.MySqlDbContextOptionsBuilder)
71: mySqlOption.CommandTimeout(10)
72: mySqlOption.EnableRetryOnFailure(10)
73: End Sub)
74: End Function, ServiceLifetime.Transient, ServiceLifetime.Transient)
75:
76: ' configure strongly typed settings object
77: Builder.Services.Configure(Of Jwt.JwtSettings)(Builder.Configuration.GetSection("JwtSetting"))
78:
79: 'configure DI for application services
80: Builder.Services.AddScoped(Of IUserService, UserService)
81: Builder.Services.AddSingleton(Of IAesCryptor, AesCryptor)
82: 'Builder.Services.AddSingleton(Of INotificationCacheService, NotificationCacheService)
83:
84: Builder.Services.AddDatabaseDeveloperPageExceptionFilter
85:
86: Builder.Services.AddIdentity(Of ApplicationUser, ApplcationRole).AddDefaultUI().AddDefaultTokenProviders().AddUserStore(Of CustomUserStore).AddRoleStore(Of CustomRoleStore)()
87:
88:
89: Builder.Services.Configure(Of IdentityOptions)(Sub(options)
90: options.SignIn.RequireConfirmedPhoneNumber = False
91: options.SignIn.RequireConfirmedEmail = False
92: options.SignIn.RequireConfirmedAccount = False
93: ''Password settings.
94: 'options.Password.RequireDigit = True
95: 'options.Password.RequireLowercase = True
96: 'options.Password.RequireNonAlphanumeric = True
97: 'options.Password.RequireUppercase = True
98: 'options.Password.RequiredLength = 6
99: 'options.Password.RequiredUniqueChars = 1
100: ''Lockout settings.
101: 'options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5)
102: 'options.Lockout.MaxFailedAccessAttempts = 5
103: 'options.Lockout.AllowedForNewUsers = True
104: ''User settings.
105: 'options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"
106: 'options.User.RequireUniqueEmail = False
107: End Sub)
108:
109: ' ******* Configure the HTTP request pipeline. *******
110: Dim App = Builder.Build
111:
112: Dim I As Integer = 0
113: Builder.Services.OrderBy(Function(Z) Z.Lifetime.ToString).ThenBy(Function(Z) Z.ServiceType.ToString).Distinct.ToList.ForEach(Sub(X)
114: I = I + 1
115: Dim ST As Type = X.ImplementationType
116: If ST IsNot Nothing Then
117: Dim ServiceInstance = Nothing
118: Try
119: ServiceInstance = App.Services.GetRequiredService(ST)
120: Catch ex As System.InvalidOperationException
121: End Try
122: Debug.WriteLine($"{I}. {X.Lifetime} : {IIf(ServiceInstance IsNot Nothing, "HasInstance", " ")} : {X.ServiceType}")
123: End If
124: End Sub)
125: 'Scoped : one instance per web request
126: 'Transient : each time the service is requested, a new instance is created
127:
128: LoggerFactory = App.Services.GetRequiredService(Of ILoggerFactory)
129:
130: If App.Environment.IsDevelopment Then
131: 'The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
132: Else
133: App.UseExceptionHandler("/Home/Error")
134: App.UseHsts 'HTTPS Redirection Middleware (UseHttpsRedirection) to redirect HTTP requests to HTTPS. HSTS Middleware (UseHsts) to send HTTP Strict Transport Security Protocol (HSTS) headers to clients.
135: End If
136:
137: App.UseForwardedHeaders(New ForwardedHeadersOptions With {
138: .ForwardedHeaders = ForwardedHeaders.XForwardedFor Or ForwardedHeaders.XForwardedProto
139: })
140:
141: If Builder.Environment.IsDevelopment Then App.UseDeveloperExceptionPage
142:
143: App.UseRouting
144:
145: '--- from Identity project
146: App.UseHttpsRedirection
147: App.UseStaticFiles(New StaticFileOptions With {.FileProvider = New PhysicalFileProvider(Builder.Configuration("StaticFilesRoot"))})
148: App.UseStaticFiles(New StaticFileOptions With {.FileProvider = New PhysicalFileProvider(Builder.Configuration("StaticFilesRoot")), .RequestPath = "/Identity"})
149: App.UseAuthentication
150: App.UseAuthorization
151: App.MapControllerRoute(name:="areas", pattern:="{area:exists}/{controller=Account}/{action=Index}/{id?}")
152: App.MapControllerRoute(name:="default", pattern:="{controller=Home}/{action=Index}/{id?}")
153:
154: App.MapRazorPages
155: '----------------------
156:
157: 'Route anylizer
158: App.Use(Async Function(context As HttpContext, NextRequestDelegate As RequestDelegate)
159: Dim CurrentEndpoint = context.GetEndpoint()
160: If (CurrentEndpoint Is Nothing) Then
161: Debug.WriteLine($"RequestPath {context.Request.Path} endpoint nothing.")
162: Dim StaticOptions As StaticFileOptions = New StaticFileOptions With {.FileProvider = New PhysicalFileProvider(Builder.Configuration("StaticFilesRoot"))}
163: Dim Opt As IOptions(Of StaticFileOptions) = Options.Create(StaticOptions)
164: Dim NewEnvironment As IWebHostEnvironment = Environment
165: NewEnvironment.ContentRootPath = Builder.Configuration("StaticFilesRoot")
166: NewEnvironment.WebRootPath = Builder.Configuration("StaticFilesRoot")
167: Dim StaticMiddleware = New StaticFileMiddleware(
168: Async Function()
169: 'Await NextRequestDelegate(context)
170: Dim StaticFile As IFileInfo = Opt.Value.FileProvider.GetFileInfo(context.Request.Path)
171: If Not StaticFile.Exists Then
172: Await context.Response.WriteAsync("File is missing")
173: Else
174: Await context.Response.SendFileAsync(StaticFile)
175: End If
176:
177: End Function,
178: NewEnvironment,
179: Opt,
180: LoggerFactory)
181: Await StaticMiddleware.Invoke(context)
182: Else
183: Debug.WriteLine($"Endpoint: {CurrentEndpoint.DisplayName}")
184: Dim Endpoint As RouteEndpoint = TryCast(CurrentEndpoint, RouteEndpoint)
185: Debug.WriteLine($"RoutePattern: {Endpoint?.RoutePattern.RawText}")
186: For j As Integer = 0 To CurrentEndpoint.Metadata.Count - 1
187: Debug.WriteLine($"Endpoint Metadata {j}: {CurrentEndpoint.Metadata(j)}")
188: Next
189: Await NextRequestDelegate(context)
190: End If
191: End Function)
192:
193: App.UseEndpoints(Function(x) x.MapControllers)
194:
195: App.Run()
196: End Sub
197: End Module
Project has only one controller needed to work - HomeController. It call View with SignManager and UserManager.
UserController not needed in this workflow, because UserController working from Hidden MS Identity area. But we can doing something in this area, of course need firstly inject UserManager or SignManager to this controller.
In this workflow - login/logout/register is enough view logic only. Also in my case I need to change root tag for body content.
And main parts of this puzzle - CustomUserStore and CustomRoleStore I have uploaded to GitHub https://github.com/Alex-1557/CustomIdentityInsteadDefaultIdentity.
|