Cookie Authentication In ASP.NET Core 2.0

In a previous post, I talked about getting Cookie Authentication up and running in ASP.net Core 1.X. In ASP.net Core 2.0, there has been a couple of changes to the API that are pretty easy to trip up on. Most of the changes are just a simple naming difference, but it can be pretty infuriating following a tutorial where one word trips you up! So let’s go!

Firstly, remember that this tutorial is for adding authentication to your app when you don’t want to use the out of the box “identity” services provided by ASP.net Core. It’s a very simple way to provide authentication cookies based on any logic you want, with any backing database you want.

Setup

If you are creating a new project, ensure that the “Authentication Type” when creating the project in Visual Studio is set to No Authentication. The other authentication types you can pick revolve around the inbuilt identity service, but since we are looking to do something more custom we don’t want this.

With your project up and going. In your nuget package manager, install the following package :

Install-Package Microsoft.AspNetCore.Authentication.Cookies

In the configure method of your startup.cs. Add the following line. Note that it should always come above your call to “UseMVC”, and likely above any other middleware calls that will return a result. Middleware runs in order, so you obviously want authentication to kick in before your MVC pipeline does!

app.UseAuthentication();

In the ConfigureServices method of your startup.cs, you need to add the Cookie Authentication services with their configuration. It should look pretty similar to the following :

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options => {
            options.LoginPath = "/Account/Login/";
        });

Where the Login Path is the URL to your login page. Remember this page should be excluded from authorization checks!

Disabling Automatic Challenge

Microsoft has decided that any time you access an endpoint that should be authorized, you should be redirected to the login page regardless. In ASP.net Core 1.X, this was not the case. You could set a flag called “AutomaticChallenge” to false. If you do not wish to always redirect the user (e.g. You would prefer to simply return a 401 response code – a Web API using shared Cookie Authentication is a good example where this would be relevant), you can override the redirect logic like so :

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
	.AddCookie(options => {
		options.Events.OnRedirectToLogin = (context) =>
		{
			context.Response.StatusCode = 401;
			return Task.CompletedTask;
		};
	});

This overrides the redirect logic and instead returns a simple 401. There are other events you may wish to override at the same time (For example the UnAuthorized redirect etc).

Registering A User

In exactly the same manner as Cookie Authentication in ASP.net Core 1.X, registering a user will be entirely on you and will live outside any authentication code provided out of the box by Microsoft.

Logging In A User

For logging in a user, let’s create a quick model.

public class LoginModel
{
	public string Username { get; set; }
	public string Password { get; set; }
}

Now let’s create a controller called “Account” and create a POST method that accepts our login.

public class AccountController : Controller
{
	[HttpGet]
	public IActionResult Login()
	{
		return View();
	}

	[HttpPost]
	public async Task<IActionResult> Login(LoginModel loginModel)
	{
		if(LoginUser(loginModel.Username, loginModel.Password))
		{
			var claims = new List<Claim>
			{
				new Claim(ClaimTypes.Name, loginModel.Username)
			};

			var userIdentity = new ClaimsIdentity(claims, "login");

			ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
			await HttpContext.SignInAsync(principal);

			//Just redirect to our index after logging in. 
			return Redirect("/");
		}
		return View();
	}

	private bool LoginUser(string username, string password)
	{
		//As an example. This method would go to our data store and validate that the combination is correct. 
		//For now just return true. 
		return true;
	}
}

If you’ve followed our previous tutorial on Cookie Authentication in ASP.net Core 1.X, then this should look pretty familiar. And that’s because it’s the exact same code with one notable exception. This line here :

await HttpContext.SignInAsync(principal);

In ASP.net Core 1.X is actually

await HttpContext.Authentication.SignInAsync(principal);

So that’s the only difference here.

Authorizing Controllers

This stays the same in ASP.net Core 2.0. You can simple add the “Authorize” attribute onto any controller or action.

[Authorize]
public class HomeController : Controller
{
	......

Logging Our A User

Logging out a user stays fairly simple with one small change. The line to sign a user out inside an action is now

await HttpContext.SignOutAsync();

Previously in ASP.net Core 1.X it was :

await HttpContext.Authentication.SignOutAsync();

15 thoughts on “Cookie Authentication In ASP.NET Core 2.0”

  1. Does the “Viewing The Logged In User” from the 1.0 tutorial also work the same here? I am trying but not getting anything in loggedInUser

    Reply
  2. I tried this on my Ang4 + ASP.NET Core app, but I get this error: “InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.”

    Reply
    • Just tried this from scratch and right you are. It seems that in .NET Core 2, it’s very sensitive to the AuthenticationScheme names. So try this, change your services line to something like this :

      services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
      .AddCookie(options =>
      {
      options.LoginPath = "/Account/Login/";
      });

      Most notable is the call to CookieAuthenticationDefaults.AuthenticationScheme which is just a constant for the CookieAuthentication Scheme. Then the Authorize call seems to work. You will also need to change all calls to SignIn/Signout that explicitly specify a scheme to use either this new constant, or none at all seems to work also.

      Reply
  3. Hello,

    Thanks for the tutorial, it helped me a lot.
    I just wanted to know, if the user doesn’t sign out (so SignOutAsync doesn’t get called), does the cookie expire?

    Thanks.

    Reply
  4. Hi, I can get everything to work but getting the user info to the code behind. I did everything you posted, even copy-pasted at some point. The only thing you didn’t mention was the view. But no problem, I created the inputs and button for testing, but it still didn’t work.

    What am I missing here? I’m guessing there’s some other way to bind the data or something, because I’m not getting it on C#. Id appreciate some help.

    Reply
      • Hey, Wade.

        No need. I just figure out where the problem was. The form I was talking about having created, I created it on a view for another controller/action. I just realized that it actually makes sense for it to be on the actual login page. It’s working fine now.

        Thanks.

        Reply
      • Hey, again, Wade.

        I don’t know if I said it but thanks for the great tutorial. I’d been searching so long for this and it’s finally done.

        I just have one more question. When I logout I can the respective method. It gets called. The user logs out. Great. But then I try to redirect to the ReturnUrl parameter query string and it apparently doesn’t exist… Why?

        I want to do this on a button click. I redirect him to the method but it has no query strings.

        Reply
    • Hi, Vinicius. Have you managed to login yet? If so, you can get it by typing User.Identity.Name (if you have only one user logged in, otherwise you can get to it by typing User.Identities.ToList()[0].Name… I exemplefied if the index 0 but you would have to go through a loop to find the one you want).

      Hope I’ve helped. 🙂

      Reply
  5. Hello,
    I’m new to ASP.NET and I’m having problems implementing this with my own little project. I’d like to see this implemented in a simple runnable project if possible. Thanks

    Reply

Leave a Comment