ASP.NET Core 2.2 For Beginners (Part 10): Forms Authentication

In this chapter, you will learn about ASP.NET Identity and how you can use it to implement registration and login in your application. You will add the authentication from scratch to learn how all the pieces fit together.

ASP.NET Identity is a framework that you need to install either with the NuGet Manager or by adding it manually in the .csproj file. It can handle several types of authentication, but in this chapter, you will focus on Forms Authentication.

The first thing you need to add is a User entity class that inherits from an identity base class, which gives you access to properties such as Username, PasswordHash, and Email. You can add as many properties to the User class as your application needs, but in this chapter, you will only use some of the inherited properties.

The User class needs to be plugged into a class called UserStore, provided by the Identity framework. It is used when creating and validating a user that then is sent to a database; Entity Framework is supported out of the box. You can implement your own UserStore, for a different database provider.

The User class needs to be plugged into an IdentityDb class that handles all communica­tion with an Entity Framework-supported database, through an Entity Framework DbCon­text. The way this is done is by making your existing VideoDbContext inherit from the IdentityDbContext class instead of the current DbContext class.

The UserStore and the IdentityDbContext work together to store user information and validate against the hashed passwords in the database.

Another class involved in the process is the SignInManager, which will sign in a user once the password has been validated. It can also be used to sign out already logged in users. A cookie is used to handle Forms Authentication sign-in and sign-out. The cookie is then sent with every subsequent request from the browser, so that the user can be identified.

The last piece is the Identity Middleware that needs to be configured to read the cookie and verify the user.

The [Authorize] attribute can be applied to a controller to restrict user access; a user must be signed in and verified to have access to the actions in that controller.

The [AllowAnonymous] attribute can be applied to actions to allow any visitor access to that action, even if they aren’t registered or signed in.

You can use parameters with the [Authorize] attribute to restrict the access even beyond being logged in, which is its default behavior. You can, for instance, add the Roles parameter to specify one or more roles that the user must be in to gain access.

You can also place the [Authorize] attribute on specific actions, instead of on the controller class, to restrict access to specific actions.

Adding the Authorize and AlowAnonymous Attributes

Let’s start by adding the [Authorize] attribute to the HomeController class, to grant access only to logged in users. Let’s also add the [AllowAnonymous] attribute to the Index action, so that any visitor can see the video list.

  1. Open the HomeController class and add the [Authorize] attribute to it. The [Authorize] attribute is located in the AspNetCore.Authorization namespace.

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

  1. Add the [AllowAnonymous] attribute to the Index

[AllowAnonymous]
public ViewResult Index()
{
    ...
}

  1. Save all files and navigate to the root URL in the browser. As you can see, the [AllowAnonymous] attribute lets you see the video list in the Index
  2. Click the Edit link to edit a video. Instead of being greeted by the Edit view, an error message is displayed. This confirms that the [Authorize] attribute is working. You are not logged in, and are therefore not allowed to use the Edit

Configuring the Identity Framework

Once you have changed the inheritance on the VideoDbContext from the current DbCon­text to the IdentityDbContext, the Identity services can be configured in the Configure­Services method, and in the Identity middleware installed in the Configure method, in the Startup class.

The services that need to be configured are the UserStore and SignInManager.

  1. Add the VideoUser entity class to the Entities folder and inherit from the IdentityUser class to gain access to its user properties. Add a using statement to the AspNetCore.Identity namespace to get access to the IdentityUser class. It’s in the VideoUser class that you can add your own user properties, specific to your application; it could be any property related to the user. Below is a list of all the properties the IdentityUser class will bring.

public class VideoUser : IdentityUser { }

  1. Open the VideoDbContext and make it inherit the IdentityDbContext class instead of EFs default DbContext. You can specify the type of user it should store, which in this case is the VideoUser entity class you just added. The IdentityDbContext class is located in the AspNetCore.Identity.EntityFrameworkCore namespace.

public class VideoDbContext : IdentityDbContext<VideoUser>
{
   ...
}

  1. Open the Startup class and add using statements to the Entities and Identity namespaces to get access to the VideoUser and IdentityRole classes, and then locate the ConfigureServices

using AspNetCore22Intro.Entities;
using Microsoft.AspNetCore.Identity;

  1. Add the Identity service to the services collection by calling the AddIdentity The method takes two generic type parameters: the first is the user you want it to use (the VideoUser entity class you just added) and the second is the identity role you want it to use (use the built-in IdentityRole class). You can inherit the IdentityRole class to another class if you want to implement your own identity role behavior. Add the service above the AddMvc method call.

services.AddIdentity<VideoUser, IdentityRole>();

  1. You must also install the Entity Framework Stores services that handle creation and validation of users against the database. You need to provide the VideoDbContext to it, so that it knows which context to use when communicating with the database. You can use the fluent API to call the AddEntityFrameworkStores method on the AddIdentity

services.AddIdentity<VideoUser, IdentityRole>()
    .AddEntityFrameworkStores<VideoDbContext>();

  1. Next you need to install the middleware components in the Configure The location of the middleware is important. If you place it too late in the pipeline, it will never be executed. Place it above the MVC middleware to make it available to the MVC framework.

app.UseAuthentication();

  1. Build the application with Ctrl+Shift+B to make sure that it builds correctly.

 

The complete VideoUser class:

public class VideoUser : IdentityUser
{
}

Creating the AspNet Identity Database Tables

Now that the configuration is out of the way, it is time to create a new migration that adds the necessary AspNet identity tables to the database.

  1. Open the Package Manager Console and execute the following command to create the necessary migration file: add-migration IdentityTables
  2. Execute the following command to create the identity tables in the database: update-database
  3. Open the SQL Server Object Explorer and drill down to the tables in your VideoCoreIntroDb

User Registration

Now that all the configuration and database table creation is done, it is time to focus on how a user can register with the site.

If you run the application as it stands right now, the Identity middleware will redirect to the /Account/Login URL, which doesn’t exist yet. Instead, the next piece of middleware handles the request, and the message Hello from configuration will be displayed in the browser.

To display a Login view, you must add an AccountController class with a Login action. And to log in, the user needs to register. You therefore must implement a Register view, and a Register action in the AccountController class.

  1. Open the cs file and add the following three middleware components to the HTTP pipeline before the MVC middleware in the Configure method to enable authentication and HTTPS redirection.

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();

  1. Add a class named AccountController to the Controllers folder and let it inherit the Controllers class located in the AspNetCore.Mvc namespace.

public class AccountController : Controller
{
}

  1. Add an HTTP GET Register action to the class. The view doesn’t have to receive a model with data, because the user will supply all the registration information in the view.

[HttpGet]
public IActionResult Register()
{
    return View();
}

  1. Add a class called RegisterViewModel in the ViewModels This will be the view model for the Register view.
  2. Add a using statement to the ComponentModel.DataAnnotations namespace to get access to the necessary data annotation attributes.
  3. Add a string property called Username that is required and has a maximum of 255 characters. The length is determined by the max number of characters that the AspNetUser table can store for a username.

[Required, MaxLength(255)]
public string Username { get; set; }

  1. Add a string property called Password that is required and has the Password data type.

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

  1. Add a string property called ConfirmPassword that has the Password data type and uses the Compare attribute to compare its value with the Password You can use the C# nameof operator to specify the compare property, instead of using a string literal.

[DataType(DataType.Password), Compare(nameof(Password))]
public string ConfirmPassword { get; set; }

  1. Add a new folder called Account inside the Views This folder will hold all the views related to the Account controller.
  2. Add a Razor View view called Register to the Account
  3. Delete all the content in the view.
  4. Add an @model directive for the RegisterViewModel

@model RegisterViewModel

  1. Use the ViewBag to add the Register to the Title

@{ ViewBag.Title = "Register"; }

  1. Add an <h1> heading with the text Register.
  2. Add a <form> that posts to the Register action in the Account Use Tag Helpers to create the form.

<form method="post" asp-controller="Account" asp-action="Register"></form>

  1. Add a validation summary that only displays errors related to the model.

<div asp-validation-summary="ModelOnly"></div>

  1. Add a <div> that holds a <label> and an <input> for the Username model property and a <span> for the validation.

<div>
    <label asp-for="Username"></label>
    <input asp-for="Username" />
    <span asp-validation-for="Username"></span>
</div>

  1. Repeat step 16 for the Password and ConfirmPassword properties in the model.
  2. Add a submit button inside a <div> to the form. Assign the text Register to the value

<div>
    <input type="submit" value="Register" />
</div>

  1. Open the AccountController
  2. Add a using statement to the ViewModels namespace to get access to the RegisterViewModel

using AspNetCore22Intro.ViewModels;

  1. Add an HTTP POST Register action that will be called by the form when the submit button is clicked. It should return an IActionResult and take a RegisterViewModel parameter called model. The action must be asynchronous to await the result from the UserManager and SignInManager, which you will inject into the controller later.

[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
}

  1. The first thing to do in any HTTP POST action is to check if the model state is valid; if it’s not, then the view should be re-rendered.

if (!ModelState.IsValid) return View();

  1. Add a using statement to the Entities and Identity namespaces to get access to the VideoUser, UserManager, and SignInManager
  2. Create a new instance of the VideoUser entity class and assign its Username property value from the passed-in model, below the if-statement.

var user = new VideoUser { UserName = model.Username };

  1. To work with the user entity, you need to bring in the UserManager and the SignInManager via the constructor, using dependency injection. Add a constructor to the controller and inject the two classes mentioned above.

private UserManager<VideoUser> _userManager;
private SignInManager<VideoUser> _signInManager;

public AccountController(UserManager<VideoUser> userManager, SignInManager<VideoUser> signInManager)
{
    _userManager = userManager;
    _signInManager = signInManager;
}

  1. Next you want to use the UserManager in the HTTP POST Register action to create a new user. Save the result in a variable called result.

var result = await _userManager.CreateAsync(user, model.Password);

  1. If the user was created successfully you want to sign in that user automatically. Use the Succeeded property on the result variable to check if the user was created successfully, and the SignInAsync method on the SignInManager to sign in the user. The second parameter of the method determines if the cookie should be persisted beyond the session or not.

if (result.Succeeded)
{
    await _signInManager.SignInAsync(user, false);
    return RedirectToAction("Index", "Home");
}

  1. If the user wasn’t created, you want to add the errors to the ModelState object, so that they are sent to the client as model errors, displayed in the validation summary.

else
{
    foreach (var error in result.Errors)
        ModelState.AddModelError("", error.Description);
}

  1. Return the view below the else-block.

return View();

  1. Save all the files and navigate to the /Account/Register The Register View should be displayed. Fill out the form with a three-letter password and click the Register button. The validation summary should display the errors that were looped into the ModelState object, in the Register action method.
  2. Fill out the form (with correct information this time). You should be redirected to the Index view through the RedirectToAction method in the Register
  3. View the data in the AspNetUsers table in the SQL Server Object Explorer to verify that the user was registered.

The complete code for the RegisterViewModel class:

public class RegisterViewModel
{
    [Required, MaxLength(255)]
    public string Username { get; set; }

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

    [DataType(DataType.Password), Compare(nameof(Password))]
    public string ConfirmPassword { get; set; }
}

 

The complete code for the AccountController class:

public class AccountController : Controller
{
    private readonly UserManager<VideoUser> _userManager;
    private readonly SignInManager<VideoUser> _signInManager;

    public AccountController(UserManager<VideoUser> userManager, SignInManager<VideoUser> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    [HttpGet]
    public IActionResult Register()
    {
        return View();
    }

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

        var user = new VideoUser { UserName = model.Username };
        var result = await _userManager.CreateAsync(user, model.Password);

        if (result.Succeeded)
        {
            await _signInManager.SignInAsync(user, false);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            foreach (var error in result.Errors)
                ModelState.AddModelError("", error.Description);
        }

        return View();
    }
}

 

The complete code for the Register view:

@model RegisterViewModel
@{ ViewBag.Title = "Register"; }

<h1>Register</h1>

<form method="post" asp-controller="Account" asp-action="Register">
    <div asp-validation-summary="ModelOnly"></div>

    <div>
        <label asp-for="Username"></label>
        <input asp-for="Username" />
        <span asp-validation-for="Username"></span>
    </div>

    <div>
        <label asp-for="Password"></label>
        <input asp-for="Password" />
        <span asp-validation-for="Password"></span>
    </div>

    <div>
        <label asp-for="ConfirmPassword"></label>
        <input asp-for="ConfirmPassword" />
        <span asp-validation-for="ConfirmPassword"></span>
    </div>

    <div>
        <input type="submit" value="Register" />
    </div>
</form>

Login and Logout

In this section, you will implement login and logout in your application. The links will be added to a partial view called _LoginLinks that you will add to the Views/Shared folder. The partial view will then be rendered from the _Layout view using the @Partial or @PartialAsync method.

When an anonymous user arrives at the site, Login and Register links should be available. When a user has logged in or registered, the username and a Logout link should be visible.

You must also create a new view called Login in the Views/Account folder, a view that the Login link opens by calling a Login action in the Account controller.

To work with users and sign-in information in views, you inject the SignInManager and UserManager, similar to the way you use dependency injection in methods and construc­tors in classes.

When an anonymous user clicks a restricted link, like the Edit link, a ReturnUrl parameter is sent with the URL, so that the user will end up on that view when a successful login has been made. When creating the LoginViewModel you must add a property for the return URL, so that the application can redirect to it. Below is an example URL with the ReturnUrl parameter.

https://localhost:44341/Account/Login?ReturnUrl=%2FHome%2FEdit%2F1

Adding the _Login Partial View

This partial view will contain the Login and Register links that will be visible when an anonymous user visits the site, and a Logout link and the username when the user is logged in.

  1. Add a Razor View called _LoginLinks to the Views/Shared
  2. Delete all the code in the view.
  3. Add a using statement to the AspNetCore.Identity namespace to get access to the SignInManager and UserManager.

@using Microsoft.AspNetCore.Identity

  1. Inject the SignInManager and UserManager to the view, below the using

@inject SignInManager<VideoUser> SignInManager
@inject UserManager<VideoUser> UserManager

  1. Add if/else-blocks that check if the user is signed in, using the IsSignedIn method on the SignInManager passing it the User

@if (SignInManager.IsSignedIn(User))
{
    // Signed in user
}
else
{
    // Anonymous user
}

  1. Add a <div> that displays the username to the Signed in user-block. Use the User object’s Identity

<div>@User.Identity.Name</div>

  1. Add a form to the Signed in user-block that posts to the /Account/Logout action when a submit button is clicked.

<form method="post" asp-controller="Account" asp-action="Logout">
    <input type="submit" value="Logout" />
</form>

  1. Add two anchor tags to the Anonymous user block that navigates to the Login and Register actions in the Account

<a asp-controller="Account" asp-action="Login">Login</a>
<a asp-controller="Account" asp-action="Register">Register</a>

  1. Open the _Layout view and add a <div> above the @RenderBody method in the <body> element.
  2. Call the @Html.PartialAsync method to render the _LoginLinks partial view in the <div>.

<div>
    <partial name="_LoginLinks"/>
</div>

  1. Start the application without debugging (Ctrl+F5). Because you were signed in when registering, the username and a Logout button should be visible. Later when you have implemented the Logout action, the Login and Register links should be visible at the top of the view when logged out.

The complete code for the _LoginLinks partial view:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<VideoUser> SignInManager
@inject UserManager<VideoUser> UserManager

@if (SignInManager.IsSignedIn(User))
{
    // Signed in user
    <div>@User.Identity.Name</div>
    <form method="post" asp-controller="Account" asp-action="Logout">
        <input type="submit" value="Logout" />
    </form>
}
else
{
    // Anonymous user
    <a asp-controller="Account" asp-action="Login">Login</a>
    <a asp-controller="Account" asp-action="Register">Register</a>
}

 

The complete code for the _Layout view:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        <div>
            <partial name="_LoginLinks"/>
        </div>
        @RenderBody()
    </div>
    <footer>
        @RenderSection("footer", false)
        @await Component.InvokeAsync("Message")
    </footer>
</body>
</html>

Adding the Logout Action

The SignOutAsync method on the SignInManager must be called to log out a user when the Logout button is clicked. The Logout action in the Account controller must be asyn­chronous because the SignOutAsync method is asynchronous.

  1. Open the AccountController
  2. Add an async HTTP POST action called Logout that returns a Task<IActionResult>. This action will be called when the Logout link is clicked.

[HttpPost]
public async Task<IActionResult> Logout() { }

  1. Call the SignOutAsync method on the _signInManager object inside the Logout

await _signInManager.SignOutAsync();

  1. Because the user is logging out, you want the user to end up on a safe view after the logout process has completed. Add a redirect to the Index action in the Home

return RedirectToAction("Index", "Home");

The complete code for the Logout action:

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

Adding the LoginViewModel Class

This model is responsible for passing the login information provided by the user, and the ReturnUrl URL parameter value, to the HTTP POST Login action.

The model needs four properties: Username, Password, RememberMe, and ReturnUrl. The RememberMe property determines if the cookie should be a session cookie or if a more persistent cookie should be used.

  1. Add a new class called LoginViewModel to the ViewModels
  2. Add a using statement to the DataAnnotations namespace to get access to the data annotation attributes.

using System.ComponentModel.DataAnnotations;

  1. Add three string properties called Username, Password, and ReturnUrl, and a bool property called RememberMe.
  2. Add the Required attribute to the Username

[Required]

  1. Add the Password and Required attributes to the Password property.

[DataType(DataType.Password), Required]

  1. Use the Display attribute to change the label text to Remember Me for the ReturnUrl

[Display(Name = "Remember Me")]

 

The complete code for the LoginViewModel class:

public class LoginViewModel
{
    [Required]
    public string Username { get; set; }

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

    [Display(Name = "Remember Me")]
    public bool RememberMe { get; set; }
}

Adding the HTTP GET Login Action

This action will be called when the user clicks the Login link. You will need to create an instance of the LoginViewModel and assign the return URL, passed into the action, to its ReturnUrl property. Then pass the model to the view.

  1. Open the AccountController
  2. Add an HTTP GET action called Login that takes a string parameter called returnUrl and returns an IActionResult.

[HttpGet]
public IActionResult Login(string returnUrl ="")
{
}

  1. Create an instance of the LoginViewModel and assign the return URL passed into the action to its ReturnUrl

var model = new LoginViewModel { ReturnUrl = returnUrl };

  1. Return the model with the view.

return View(model);

The complete code for the HTTP GET Login action:

[HttpGet]
public IActionResult Login(string returnUrl ="")
{
    var model = new LoginViewModel { ReturnUrl = returnUrl };
    return View(model);
}

Adding the HTTP POST Login Action

The HTTP POST Login action will be called when the user clicks the Login button in the Login view. The view’s login form will send the user data to this action; it therefore must have a LoginViewModel as a parameter. The action must be asynchronous because the PasswordSignInAsync method provided by the SignInManager is asynchronous.

  1. Open the AccountController
  2. Add an async HTTP POST action called Login that takes an instance of the LoginViewModel as a parameter and returns a Task<IActionResult>.

[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
}

  1. The first thing to do in any HTTP POST action is to check if the model state is valid; if it’s not, then the view should be re-rendered.

if (!ModelState.IsValid) return View(model);

  1. Sign in the user by calling the PasswordSignInAsync method, passing in the username, password, and remember me values. Store the result in a variable called result. The last parameter determines if the user should be locked out, if providing wrong credentials.

var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, false);

  1. Add an if-statement checking if the sign-in succeeded.

if (result.Succeeded)
{
}

  1. Add another if-statement, inside the previous one, that checks that the URL isn’t null or empty and that it is a local URL. It is important to check if it is a local URL, for security reasons. If you don’t do that your application is vulnerable to attacks.

if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
{
}
else
{
}

  1. If the return URL exists and is safe, then redirect to it in the if-block.

return Redirect(model.ReturnUrl);

  1. If the URL is empty or isn’t local, then redirect to the Index action in the Home

return RedirectToAction("Index", "Home");

  1. Add a ModelState error and return the view with the model below it. Place the code below the outer if-statement, to be certain that it only is called if the login is unsuccessful.

ModelState.AddModelError("", "Login failed");
return View(model);

 

The complete code for the HTTP POST Login action:

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

    var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, false);

    if (result.Succeeded)
    {
        if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
        {
            return Redirect(model.ReturnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

    ModelState.AddModelError("", "Login failed");
    return View(model);
}

Adding the Login View

You need to add a view called Login to the Account folder, to enable visitors to log in.

  1. Add a Razor View view called Login to the Views/Account
  2. Delete all the content in the view.
  3. Add an @model directive for the LoginViewModel

@model LoginViewModel

  1. Use the ViewBag to add a title with the text Login.

@{ ViewBag.Title = "Login"; }

  1. Add an <h2> heading with the text Login.
  2. Add a <form> that posts to the Login action in the Account Use Tag Helpers to create the form, and to return the return URL.

<form method="post" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl"></form>

  1. Add a validation summary that only displays errors related to the model.

<div asp-validation-summary="ModelOnly"></div>

  1. Add a <div> that holds a <label> and an <input> for the Username model property, and a <span> for the validation.

<div>
    <label asp-for="Username"></label>
    <input asp-for="Username" />
    <span asp-validation-for="Username"></span>
</div>

  1. Repeat step 8 for the Password and RememberMe properties in the model.
  2. Add a submit button with the text Login to the form; place it inside a <div>.

<div>
    <input type="submit" value="Login" />
</div>

  1. Start the application without debugging (Ctrl+F5). Log out if you are signed in.
  2. Click the Edit link for one of the videos. The Login view should be displayed because you are an anonymous user. Note the ReturnUrl parameter in the URL.
  3. Log in as a registered user. The Edit view, for the video you tried to edit before, should open. Note the username and the Logout button at the top of the view.
  4. Click the Logout button to log out the current user. You should be taken to the Index Note the Login and Register links at the top of the view.

The complete markup for the Login view:

@model LoginViewModel
@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>

<form method="post" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">

    <div asp-validation-summary="ModelOnly"></div>

    <div>
        <label asp-for="Username"></label>
        <input asp-for="Username" />
        <span asp-validation-for="Username"></span>
    </div>

    <div>
        <label asp-for="Password"></label>
        <input asp-for="Password" />
        <span asp-validation-for="Password"></span>
    </div>

    <div>
        <label asp-for="RememberMe"></label>
        <input asp-for="RememberMe" />
        <span asp-validation-for="RememberMe"></span>
    </div>

    <div>
        <input type="submit" value="Login" />
    </div>
</form>

Summary

In this chapter, you used ASP.NET Identity to secure your application, implementing regis­tration and login from scratch.

The first thing you did was to add a VideoUser entity class that inherited the IdentityUser base class. This gave you access to properties such as Username, PasswordHash, and Email.

Then you plugged the VideoUser entity into a UserStore and an IdentityDb class. This made it possible to create and validate a user, which then was stored in the database.

Then you added middlware to handle HTTPS redirection and user authentication.

The UserManager and SignInManager were then used to implement registration and login for users, with a cookie that handles the Forms Authentication.

The [Authorize] and [AllowAnonymous] attributes were used to restrict user access to controller actions.

You also added views to register, log in, and log out a user.

In the next chapter, you will use front-end frameworks to style the application.

Stay connected with news and updates!

Join our mailing list to receive the latest news and updates from our team.
Don't worry, your information will not be shared.

Subscribe
Close

50% Complete

Two Step

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.