Search code examples
asp.netasp.net-coreasp.net-identityasp.net-authorization

Custom Authentication using legacy user table in Asp.Net Core


I need to authenticate users from an existing legacy Users Table, but I would like to do it on the shoulders of Identity and Authorization, if possible, so that the features and attributes are at my disposal. Most of our users already have logins from a much older native application that we want to carry over to our Asp.net Core Intranet Web App. I've seen a few topics on this issue on SO and other places, but none seem to suit my specific needs.

What is the best way I should go about this?


Solution

  • Most of our users already have logins from a much older native application that we want to carry over to our Asp.net Core Intranet Web App.

    You could use OWIN cookie authentication middleware. The middleware is changed a little bit in ASP.NET Core.

    FYI: Microsoft is moving toward policy based Authorization instead of Role base. Role claim is there for backward compatibility. If you plan to use AuthorizeAttribute with role, you might want to look at this sample and usage.

    https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/

    Login Action Method

    Inside Login action method, we first authenticate with existing authentication mechanism. Then pass user information and rolenames to SignManager method.

    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
    {
        ViewData["ReturnUrl"] = returnUrl;
        if (ModelState.IsValid)
        {
            bool result = // Validate user
            if (result)
            {
                var user = await _userRepository.GetUserByUserNameAsync(model.UserName);
                var roleNames = (await _roleRepository.GetRolesForUser(user.Id))
                     .Select(r => r.Name).ToList();
                await _signInManager.SignInAsync(user, roleNames);            
            }
            else
            {
                ModelState.AddModelError("", "Incorrect username or password.");
            }
        }
        return View("Login", model);
    }
    

    SigIn Manager

    Then we create user's information as claims.

    public class SignInManager
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public SignInManager(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        public async Task SignInAsync(User user, IList<string> roleNames)
        {
            var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Sid, user.Id.ToString()),
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.GivenName, user.FirstName),
                    new Claim(ClaimTypes.Surname, user.LastName)
                };
    
            foreach (string roleName in roleNames)
            {
                claims.Add(new Claim(ClaimTypes.Role, roleName));
            }
    
            var identity = new ClaimsIdentity(claims, "local", "name", "role");
            var principal = new ClaimsPrincipal(identity);
    
            await _httpContextAccessor.HttpContext.Authentication
                 .SignInAsync(Constants.AuthenticationScheme, principal);
        }
    
        public async Task SignOutAsync()
        {
            await _httpContextAccessor.HttpContext.Authentication
                .SignOutAsync(Constants.AuthenticationScheme);
        }
    }
    

    Startup.cs

    Last but not least, we then add cookie authentication middleware in Startup.cs.

    public void Configure(IApplicationBuilder app, 
           IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ...   
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            Events = new CookieAuthenticationEvents
            {
                OnRedirectToAccessDenied = context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                    return TaskCache.CompletedTask;
                }
            },
            AuthenticationScheme = Constants.AuthenticationScheme,
            LoginPath = new PathString("/Account/Login"),
            AccessDeniedPath = new PathString("/Common/AccessDenied"),
            AutomaticAuthenticate = true,
            AutomaticChallenge = true
        });
        ...
    }