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

The default ASP.NET Core 2 web template provides lots of code to authenticate users. My screen is not big enough to display all the files in the solution explorer. The template offers lots of functionalities: users can log in using username/password or using an external provider such as Google or Microsoft. You can use two-factor authentication. And so on.

You can rely on this code and use it as is. Or, if you just want to use cookie authentication, you can delete all these files, and create the sign up and log in pages by yourself. As you'll see in this post, the cookie authentication is very easy to use 😃

Register the authentication middlewares

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

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 the authentication functionalities 😃

Create the LogIn model

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 has a new way to create page: Razor Pages. Razor pages provide a way to create pages in a more understandable way. But you can easily transpose this code to use a Controller by just copying the code in the controller.

I create a new page Pages/login.cshtml. The page contains a form with two fields username and password and some bootstrap classes. A Razor Page starts with @page.

@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>

Let's add some code to handle the button click. You can add the code directly in the page or create a PageModel class. The code is very short, so I'll add it in the page.

@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));

            // 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>

As you can see, it's very simple 😃 Indeed, you just need to create an identity with the user data and call HttpContext.SignInAsync(...) with the user data.

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

await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

Conclusion

The cookie authentication is very easy to use. If you don't need to use all the functionalities provided by the default template, you can consider implement them by yourself.