Cookie authentication in ASP.NET Core 2 without ASP.NET Identity

 
 
  • Gérald Barré

The default ASP.NET Core 2 web template ships with a large amount of authentication code covering many scenarios: username/password login, external providers such as Google or Microsoft, two-factor authentication, and more.

You can use the template as-is, or if you only need cookie authentication, you can strip it down and build the sign-up and login pages yourself. As this post shows, cookie authentication is straightforward to set up.

#Register the authentication middleware

First, add the NuGet package Microsoft.AspNetCore.Authentication.Cookies. Then, you must configure and register the authentication middleware.

C#
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
        .AddCookie();

    services.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
    app.UseMvc();
}

You are now ready to use cookie authentication.

#Create the LogIn model

C#
public class LogInModel
{
    [Required]
    public string Username { get; set; }

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    public bool RememberMe { get; set; }
}

#Create the LogIn page

ASP.NET Core 2 introduced Razor Pages, which provide a simpler and more organized approach to building pages. You can also adapt this code to use a controller by moving the logic into a controller action.

Create a new page at Pages/login.cshtml. The page contains a form with username and password fields, styled with Bootstrap classes. A Razor Page must start with @page.

HTML
@page
<form method="post">
    <div asp-validation-summary="All" class="text-danger"></div>

    <div class="form-group">
        <label asp-for="LogInModel.Username" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="LogInModel.Username" class="form-control" />
            <span asp-validation-for="LogInModel.Username" class="text-danger"></span>
        </div>
    </div>

    <div class="form-group">
        <label asp-for="LogInModel.Password" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="LogInModel.Password" class="form-control" />
            <span asp-validation-for="LogInModel.Password" class="text-danger"></span>
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <div class="checkbox">
                <label asp-for="RememberMe">
                    <input asp-for="RememberMe" />
                    @Html.DisplayNameFor(m => m.RememberMe)
                </label>
            </div>
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="submit" class="btn btn-default">Log In</button>
        </div>
    </div>
</form>

Next, add code to handle form submission. You can add it directly to the page or in a separate PageModel class. Since the code is short, it is added inline here.

HTML
@page

@using Microsoft.AspNetCore.Authentication;
@using Microsoft.AspNetCore.Authentication.Cookies;
@using System.Security.Claims;

@functions {
    [BindProperty] // Bind on Post
    public LogInModel LogInModel { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (ModelState.IsValid)
        {
            var isValid = true; // TODO Validate the username and the password with your own logic
            if (!isValid)
            {
                ModelState.AddModelError("", "username or password is invalid");
                return Page();
            }

            // Create the identity from the user info
            var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, LogInModel.Username));
            identity.AddClaim(new Claim(ClaimTypes.Name, LogInModel.Username));
            // You can add roles to use role-based authorization
            // identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));

            // Authenticate using the identity
            var principal = new ClaimsPrincipal(identity);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = LogInModel.RememberMe });

            return RedirectToPage("Index");
        }

        return Page();
    }
}

<form>
    ...
</form>

The process is straightforward: create a ClaimsIdentity with the user's information and call HttpContext.SignInAsync(...) to issue the authentication cookie.

Now you can sign out using a single line of code:

C#
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

#Conclusion

Cookie authentication is simple to set up. If you do not need all the features provided by the default template, rolling your own sign-in and sign-up pages is a viable and lightweight alternative.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?