How to do Authentication of Users in Identity in ASP.NET Core

How to do Authentication of Users in Identity in ASP.NET Core

Authenticating Users means processing only those requests that come from authenticated users.

Create a new Controller called ‘Home’ and change its Index Action method to return a string ‘Hello’ as Model to the default view:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace Filters.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View((object)"Hello");
        }
    }
}

Next, add Index View inside the ‘Views/Home’ folder with the following code:

@model string

<h1 class="bg-info text-white">Index</h1>
@Model

The View does nothing special except for showing the string ‘Hello’ which is returned by the Controller.

Now run your project and you will see the hello message displayed on your browser, see below image:

Index view displayed without authentication

There is no restriction for invoking the Index Action method at present. Therefore when I ran the project then my browser sent an un-authenticated request to this action. My un-authenticated request was able to invoke this action method and I see the ‘Hello’ message on my browser.

Now I will restrict the Index Action so that it can only be invoked by request that comes from Authenticated Users. So add the [[Authorize]] attribute, which resides on the ‘Microsoft.AspNetCore.Authorization’ namespace, on the Index action method:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Filters.Controllers
{
    public class HomeController : Controller
    {
        [Authorize]
        public IActionResult Index()
        {
            return View((object)"Hello");
        }
    }
}

Now run your project once again, and this time you will see a message stating – Status Code: 404; Not Found. This is shown by the image below:

index action method with authorize attribute

There are 2 things that happened here:

  • 1. I applied the [[Authorize]] attribute to the Index Action, so my un-authenticated request was not able to invoke it.
  • 2. The Identity system redirected me to the Login URL – http://localhost:57149/Account/Login?ReturnUrl=%2F, so that I can log in to authenticate myself.

Notice the URL contains the query string variable ‘ReturnUrl’ which contains ‘%2F’. Once I complete my log in procedure then I will be redirected to the URL http://localhost:57149.

Note that the ‘%2F’ is an encoded URL which stands for ‘/’.

Changing the Default Login URL in Identity

Although /Account/Login is the default Login URL in Identity system, and to where that clients are redirected to when authorization is required.

If you want to change this login URL then go to in the ConfigureServices method of the Startup class. There add the below code line:

services.ConfigureApplicationCookie(opts => opts.LoginPath = "/Authenticate /Login");

Here I have specified the new login URL as ‘/Authenticate/Login’. Note that this ULR cannot be formed by the routing system. So if you change your routing scheme then you should make sure that you change the Identity Login URL here manually.

Implementing Authentication in Identity

In order to create a login feature, I need to create a Login class inside the Models folder. This class 3 properties – Email, Password and ReturnUrl.

Email and Password properties are made ‘Required’ fields since User will have to enter both of them in order to do login.

The ReturnUrl property will contains the Return URL set by the Identity System.

The Login.cs class code is given below:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace Identity.Models
{
    public class Login
    {
        [Required]
        public string Email { get; set; }

        [Required]
        public string Password { get; set; }

        public string ReturnUrl { get; set; }
    }
}

The Identity System redirects to the Login URL which is ‘/Account/Login’, so I need to create ‘Account’ controller with ‘Login’ action method. From there Users can do the log in procedure.

So create ‘Account’ Controller in the ‘Controllers’ folder with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Identity.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace Identity.Controllers
{
         [Authorize]
    public class AccountController : Controller
    {
        private UserManager<AppUser> userManager;
        private SignInManager<AppUser> signInManager;

        public AccountController(UserManager<AppUser> userMgr, SignInManager<AppUser> signinMgr)
        {
            userManager = userMgr;
            signInManager = signinMgr;
        }

        public IActionResult Index()
        {
            return View();
        }

        [AllowAnonymous]
        public IActionResult Login(string returnUrl)
        {
            Login login = new Login();
            login.ReturnUrl = returnUrl;
            return View(login);
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Login(Login login)
        {
            if (ModelState.IsValid)
            {
                AppUser appUser = await userManager.FindByEmailAsync(login.Email);
                if (appUser != null)
                {
                    await signInManager.SignOutAsync();
                    Microsoft.AspNetCore.Identity.SignInResult result = await signInManager.PasswordSignInAsync(appUser, login.Password, false, false);
                    if (result.Succeeded)
                        return Redirect(login.ReturnUrl ?? "/");
                }
                ModelState.AddModelError(nameof(login.Email), "Login Failed: Invalid Email or password");
            }
            return View(login);
        }
    }
}

The controller class is applied with [Authorize] attribute so that it cannot be invoked by un-authenticated requests. However I have added [AllowAnonymous] attributes to the 2 action methods so that these action methods can be invoked by un-authenticated requests.

See that I have added a dependency of UserManager & SignInManager in the constructor so these objects will be provided by the Dependency Injection feature.

The associated code is given below:

private UserManager<AppUser> userManager;
private SignInManager<AppUser> signInManager;

public AccountController(UserManager<AppUser> userMgr, SignInManager<AppUser> signinMgr)
{
    userManager = userMgr;
    signInManager = signinMgr;
}

The UserManager is used to manage Users in Identity system while the SignInManager is used to perform the authentication.

Next, I added the GET version of the ‘Login’ action method. I applied the [[AllowAnonymous]] attribute on it so that the GET version of the Login action method does not require authentication. This action has a ‘returnUrl’ whose value is provided from the ‘ReturnUrl’ variable of the query sting by the Model Binding procedure.

The action method code is:

[AllowAnonymous]
public IActionResult Login(string returnUrl)
{
    Login login = new Login();
    login.ReturnUrl = returnUrl;
    return View(login);
}

The Model of Login type is returned by this action method to its default view. Notice I have set the value of the ‘ReturnUrl’ property of the Login class to the value that comes to the action’s parameter.

Next, I have added the POST version of the Login Action method where the actual authentication of the user will be done. The action method code is:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(Login login)
{
    if (ModelState.IsValid)
    {
        AppUser appUser = await userManager.FindByEmailAsync(login.Email);
        if (appUser != null)
        {
            await signInManager.SignOutAsync();
            Microsoft.AspNetCore.Identity.SignInResult result = await signInManager.PasswordSignInAsync(appUser, login.Password, false, false);
            if (result.Succeeded)
                return Redirect(login.ReturnUrl ?? "/");
        }
        ModelState.AddModelError(nameof(login.Email), "Login Failed: Invalid Email or password");
    }
    return View(login);
}

This action method has a parameter, of type ‘Login’ class, through which it receives the login values filled by the user in the form on the Login View. The method is provided with 2 attributes which are:

1. [AllowAnonymous] attribute lets it to be invoked by un-authenticated requests.
2. [ValidateAntiForgeryToken] to prevent cross-site request forgery.

First I get the User’s details from the FindByEmailAsync method of the UserManager class. This method takes the email address which I provided from the Email property of the Login class, like shown below:

AppUser appUser = await userManager.FindByEmailAsync(login.Email);

Next, I check if the AppUser object is not null. In that case I am first signing out any logged in user from the application:

await signInManager.SignOutAsync();

Then I am using the PasswordSignInAsync method of the ‘SignInManager’ class to log in the user by providing with the AppUser object and password string, like:

Microsoft.AspNetCore.Identity.SignInResult result = await signInManager.PasswordSignInAsync(appUser, login.Password, false, false);

I have provided ‘false’ value for the last 3rd and 4th parameters because I don’t want a persistence cookie for a persistence login that will remain even after I close the browser, nor i want to lock on the account when sing in fails.

This method returns SignInResult object that contains the result of the sign in procedure. Its ‘Succeeded’ property contains true value if the sign in is successful else returns false.

Finally, I check if the value of the Succeeded property is true, in that case I am returning the user to the URL contained in the ‘ReturnUrl’ property of the Login class. The code which does this work is:

if (result.Succeeded)
    return Redirect(login.ReturnUrl ?? "/");

I also need the Login View from where user will do the login to their accounts. Create the ‘Login’ view inside the ‘Views/Account’ folder with the code as shown below:

@model Login

<h1 class="bg-info text-white">Login</h1>
<div class="text-danger" asp-validation-summary="All"></div>

<form asp-action="Login" method="post">
    <input type="hidden" asp-for="ReturnUrl" />
    <div class="form-group">
        <label asp-for="Email"></label>
        <input asp-for="Email" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Password"></label>
        <input asp-for="Password" class="form-control" />
    </div>
    <button class="btn btn-primary" type="submit">Log In</button>
</form>

It’s time to test the authentication feature. I already have a user with the following details:

1. Name – tom
2. Email – [email protected]
3. Password – [email protected]

I will login in with this user of mine.

If you don’t have any User then you can create it from the URL – ‘/Admin/Create’.

Run your project, and when you are redirected to the Login page, add the following information to the login form:
1. Email – [email protected]
2. Password – wrongpass

On clicking the Log In button you will see the message – Login Failed: Invalid Email or password. This is because I entered the wrong password, see the below image:

authentication failed identity

Now enter the correct password and click the Log In button. You will find the authentication is successful this time and you will be redirected to the Home controller’s Index View where you will see the ‘Hello’ message:

Index view displayed without authentication

Logout Feature in Identity

I have already created a Login feature for users, now I will create Logout feature. I create a Logout action method to the ‘AccountController’ with the following code:

public async Task<IActionResult> Logout()
{
    await signInManager.SignOutAsync();
    return RedirectToAction("Index", "Home");
}

The Logout Action is fairly simple which just uses signInManager.SignOutAsync() to do the signing out of any logged in user from the application.

The Logout Action method will be linked from the Home Controller’s Index View only, since after login the user is taken to this View only.

Update the ‘Index’ Action of the Home Controller like shown below:

@model string

<h1 class="bg-info text-white">Index</h1>
@Model

@if (User?.Identity?.IsAuthenticated ?? false)
{
    <a asp-controller="Account" asp-action="Logout" class="btn btn-danger">Logout</a>
}

Here, I have used the RazorPageBase class of the Microsoft.AspNetCore.Mvc.Razor namespace.

This class User.Identity.IsAuthenticated property gives true if the user is logged in else returns null. I have used it to show my Logout link only when the user is logged in to the application.

Run your project and log in with any user’s account. After login you will be redirected to the home page where you will see the Logout button. See the below image:

logout feature identity

Identity Cookie

Once the User is authenticated then Identity system will store a cookie in your browser. You can see this cookie in the ‘Application’ tab of your Chrome browser developer tools. See the below image:

identity cookie

This cookie is sent to the server at each HTTP Request, like when you open a URL of your application in your browser. The ASP.NET Core Identity uses this cookie sent by the browser to determine whether the user has been authenticated or not.

Note – To logout any user from Identity, simple delete this cookie by selecting this cookie and clicking the ‘X’ sign.

You can download the full codes of this tutorial from the below link:

Download

Conclusion

This completes this tutorial on Authenticating Users in Identity and now you are in a position to create your own secured areas in your website, where there will be a need to do login and logout.

Share this article -

yogihosting

ABOUT THE AUTHOR

This article has been written by the Technical Staff of YogiHosting. Check out other articles on "WordPress, SEO, jQuery, HTML" and more.