Implementing Custom Authentication in ASP.NET Core: A Complete Guide Without the Identity Framework

Dec 05, 2025 · Programming · 12 views · 7.8

Keywords: ASP.NET Core | Custom Authentication | Cookie Authentication

Abstract: This article provides a comprehensive guide to implementing custom authentication in ASP.NET Core, focusing on integrating existing user databases without relying on the Identity framework. It covers the entire process from service configuration and middleware setup to detailed implementation steps, with code examples and best practices for building secure authentication systems flexibly.

Introduction and Background

Authentication is a core feature in ASP.NET Core development, but not all projects are suited for the built-in Identity framework. When integrating existing user databases or implementing specific business logic, custom authentication schemes offer greater flexibility. Based on ASP.NET Core security documentation and practical development experience, this article systematically explains how to build a custom authentication system without using the Identity framework.

Core Concepts and Architecture Design

Custom authentication in ASP.NET Core primarily relies on middleware patterns and the Claims infrastructure. By configuring the Cookie authentication middleware, developers can control user login, logout, and authorization processes. Key components include: AuthenticationMiddleware, CookieAuthenticationHandler, and a custom UserManager class. These components work together to ensure security and scalability.

Service Configuration and Middleware Setup

In the ConfigureServices method of the Startup.cs file, authentication services must be added first. Example code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
        {
            options.LoginPath = "/Account/Login";
            options.LogoutPath = "/Account/Logout";
        });
    services.AddMvc();
    services.AddTransient(m => new UserManager(
        Configuration.GetValue<string>(DEFAULT_CONNECTIONSTRING)
    ));
}

This configuration specifies Cookie as the default authentication scheme and sets login and logout paths. In the Configure method, app.UseAuthentication() must be called to enable the authentication middleware, ensuring it executes before the MVC middleware.

Implementation of the Custom User Manager Class

The UserManager class handles user login, logout, and claims management. Here is an example implementation using Dapper:

public class UserManager
{
    private string _connectionString;

    public UserManager(string connectionString)
    {
        _connectionString = connectionString;
    }

    public async Task SignInAsync(HttpContext httpContext, UserDbModel user)
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            var dbUser = await connection.QueryFirstOrDefaultAsync<UserDbModel>(
                "sp_user_login",
                new { user.UserEmail, user.UserPassword },
                commandType: CommandType.StoredProcedure
            );

            if (dbUser != null)
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, dbUser.Id.ToString()),
                    new Claim(ClaimTypes.Name, dbUser.UserFirstName),
                    new Claim(ClaimTypes.Email, dbUser.UserEmail),
                    new Claim(ClaimTypes.Role, dbUser.UserPermissionType.ToString())
                };
                var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                var principal = new ClaimsPrincipal(identity);
                await httpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
            }
        }
    }

    public async Task SignOutAsync(HttpContext httpContext)
    {
        await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }
}

This class validates user credentials via a stored procedure and creates a claims collection including user identity and roles. The SignInAsync and SignOutAsync methods manage session handling.

Controller and Authorization Integration

In controllers, inject UserManager and handle login logic. An example AccountController is as follows:

public class AccountController : Controller
{
    private readonly UserManager _userManager;

    public AccountController(UserManager userManager)
    {
        _userManager = userManager;
    }

    [HttpPost]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        if (!ModelState.IsValid)
            return View(model);

        try
        {
            var user = new UserDbModel
            {
                UserEmail = model.Email,
                UserPassword = model.Password
            };
            await _userManager.SignInAsync(HttpContext, user);
            return RedirectToAction("Index", "Home");
        }
        catch (Exception ex)
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return View(model);
        }
    }
}

After completing these steps, the [Authorize] attribute can be used on controllers or action methods to restrict access, e.g., [Authorize] or [Authorize(Roles = "Admin")]. The system will automatically redirect unauthenticated users to the login page.

Advanced Topics and Best Practices

For more complex scenarios, refer to the Policy-Based Authorization section in the official ASP.NET Core documentation, which allows access control based on custom rules. Additionally, ensure secure Cookie settings in production, such as HTTPS and appropriate expiration times. For debugging, leverage open-source code repositories like the ASP.NET Core security modules on GitHub for deeper learning.

Conclusion and Resource Recommendations

Custom authentication in ASP.NET Core offers high flexibility for integrating legacy systems or meeting specific requirements. By combining Cookie middleware with custom logic, developers can build efficient and secure authentication flows. It is recommended to further explore official documentation on Cookie authentication and policy-based authorization for more advanced features.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.