Custom Authentication in ASP.Net-Core
From what I learned after several days of research,Here is the Guide for ASP .Net Core MVC 2.x Custom User Authentication
In Startup.cs
:
Add below lines to ConfigureServices
method :
public void ConfigureServices(IServiceCollection services){services.AddAuthentication( CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/Logout"; }); services.AddMvc(); // authentication services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; }); services.AddTransient( m => new UserManager( Configuration .GetValue<string>( DEFAULT_CONNECTIONSTRING //this is a string constant ) ) ); services.AddDistributedMemoryCache();}
keep in mind that in above code we said that if any unauthenticated user requests an action which is annotated with [Authorize]
, they well force redirect to /Account/Login
url.
Add below lines to Configure
method :
public void Configure(IApplicationBuilder app, IHostingEnvironment env){ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler(ERROR_URL); } app.UseStaticFiles(); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: DEFAULT_ROUTING); });}
Create your UserManager
class that will also manage login and logout. it should look like below snippet (note that i'm using dapper):
public class UserManager{ string _connectionString; public UserManager(string connectionString) { _connectionString = connectionString; } public async void SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false) { using (var con = new SqlConnection(_connectionString)) { var queryString = "sp_user_login"; var dbUserData = con.Query<UserDbModel>( queryString, new { UserEmail = user.UserEmail, UserPassword = user.UserPassword, UserCellphone = user.UserCellphone }, commandType: CommandType.StoredProcedure ).FirstOrDefault(); ClaimsIdentity identity = new ClaimsIdentity(this.GetUserClaims(dbUserData), CookieAuthenticationDefaults.AuthenticationScheme); ClaimsPrincipal principal = new ClaimsPrincipal(identity); await httpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal); } } public async void SignOut(HttpContext httpContext) { await httpContext.SignOutAsync(); } private IEnumerable<Claim> GetUserClaims(UserDbModel user) { List<Claim> claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id().ToString())); claims.Add(new Claim(ClaimTypes.Name, user.UserFirstName)); claims.Add(new Claim(ClaimTypes.Email, user.UserEmail)); claims.AddRange(this.GetUserRoleClaims(user)); return claims; } private IEnumerable<Claim> GetUserRoleClaims(UserDbModel user) { List<Claim> claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id().ToString())); claims.Add(new Claim(ClaimTypes.Role, user.UserPermissionType.ToString())); return claims; }}
Then maybe you have an AccountController
which has a Login
Action that should look like below :
public class AccountController : Controller{ UserManager _userManager; public AccountController(UserManager userManager) { _userManager = userManager; } [HttpPost] public IActionResult LogIn(LogInViewModel form) { if (!ModelState.IsValid) return View(form); try { //authenticate var user = new UserDbModel() { UserEmail = form.Email, UserCellphone = form.Cellphone, UserPassword = form.Password }; _userManager.SignIn(this.HttpContext, user); return RedirectToAction("Search", "Home", null); } catch (Exception ex) { ModelState.AddModelError("summary", ex.Message); return View(form); } }}
Now you are able to use [Authorize]
annotation on any Action
or Controller
.
Feel free to comment any questions or bug's.
Creating custom authentication in ASP.NET Core can be done in a variety of ways. If you want to build off existing components (but don't want to use identity), checkout the "Security" category of docs on docs.asp.net. https://docs.asp.net/en/latest/security/index.html
Some articles you might find helpful:
Using Cookie Middleware without ASP.NET Identity
Custom Policy-Based Authorization
And of course, if that fails or docs aren't clear enough, the source code is athttps://github.com/dotnet/aspnetcore/tree/master/src/Security which includes some samples.
I would like to add something to brilliant @AmiNadimi answer for everyone who going implement his solution in .NET Core 3:
First of all, you should change signature of SignIn
method in UserManager
class from:
public async void SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false)
to:
public async Task SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false)
It's because you should never use async void
, especially if you work with HttpContext
. Source: Microsoft Docs
The last, but not least, your Configure()
method in Startup.cs
should contains app.UseAuthorization
and app.UseAuthentication
in proper order:
if (env.IsDevelopment()){ app.UseDeveloperExceptionPage();}else{ app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseAuthentication();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{ endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");});