Custom Authentication in ASP.Net-Core Custom Authentication in ASP.Net-Core asp.net asp.net

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?}");});