ASP.NET Core Identity with MongoDB as Database {Detailed}

ASP.NET Core Identity with MongoDB as Database {Detailed}

In this tutorial we will implementASP.NET Core Identity with MongoDB as the database. So, the MongoDB will be the Identity Database instead of SQL Server. First, we will configure Identity to use MongoDB as the database and after that create an example project where Identity Users and Roles will be created. In the end, Login and Logout feature will be added so that users can be authenticated to secured portions on the website.

We will create a complete project from scratch and it’s source codes can be downloaded from my GitHub repository.

Configure Identity for MongoDB

Create a new ASP.NET Core (MVC) based app and name it IdentityMongo.

identity mongodb example
Database Name and Connection Strings

We will now add the name of the database, host and port values in the “appsettings.json” file. From here these values will be populated to a C# class in Startup.cs. The appsettings.json file should now look –

{
  "MongoDbConfig": {
    "Name": "Identity",
    "Host": "localhost",
    "Port": 27017
  }
}

The name of the MongoDB database is kept as “Identity” and it will operate from localhost: 27017.

Next, create a class called MongoDbConfig.cs inside the Settings folder of the app. As discussed, the MongoDB settings in the configuration file will be populated to this class.

The MongoDbConfig.cs code should look like:

using System;

namespace IdentityMongo.Settings
{
    public class MongoDbConfig
    {
        public string Name { get; init; }
        public string Host { get; init; }
        public int Port { get; init; }
        public string ConnectionString => $"mongodb://{Host}:{Port}";
    }
}

Now go to the Startup.cs and import the namespace:

using IdentityMongo.Settings;

Then in the ConfigureServices method, populate the database settings form appsettings.js to MongoDbConfig.cs as shown in the below code.

public void ConfigureServices(IServiceCollection services)
{
    var mongoDbSettings = Configuration.GetSection(nameof(MongoDbConfig)).Get<MongoDbConfig>();
    services.AddControllersWithViews();
}
Identity User and Role classes

The AspNetCore.Identity.MongoDbCore package adds MongoDb UserStore and RoleStore adapter for ASP.NET Core Identity and allows you to use MongoDB instead of SQL server. So, add this package to your app by running the following command in your Package Manager Console.

Install-Package AspNetCore.Identity.MongoDbCore
AspNetCore.Identity.MongoDbCore-package

Note that MongoDB being NoSQL does not have tables and columns but instead there are Collections. We will create Identity User and Role classes and map them to these collections.

We will now define our Identity User and Role classes. The Identity User class will inherit from MongoIdentityUser class and will be mapped to “Users” collections. The Identity Role class will inherit from MongoIdentityRole class and will be mapped to “Roles” collections.

Create a new class called ApplicationUser.cs inside the Models folder. This class will serve as Identity User class for our app. The code of this class is given below.

using AspNetCore.Identity.MongoDbCore.Models;
using MongoDbGenericRepository.Attributes;
using System;

namespace IdentityMongo.Models
{
    [CollectionName("Users")]
    public class ApplicationUser : MongoIdentityUser<Guid>
    {
    }
}

Two things should be noted here:

  1. Attriube [CollectionName("Users")] specifies that this class will be mapped to “Users” collection in MongoDB.
  2. The use of Guid for the T type in MongoIdentityUser means the primary key of the collection will be a Guid type.

Similarly, create ApplicationRole.cs inside the Models folder and it will serve as Identity Role class. The code is given below.

using AspNetCore.Identity.MongoDbCore.Models;
using MongoDbGenericRepository.Attributes;
using System;

namespace IdentityMongo.Models
{
    [CollectionName("Roles")]
    public class ApplicationRole : MongoIdentityRole<Guid>
    {

    }
}
Registering Identity in Startup class

The final thing we need to do is register ASP.NET Core Identity in Startup class ConfigureServices method.

Import the namespaces of the ApplicationUser and ApplicationRole classes.

using IdentityMongo.Models;

Then register Identity to use these classes for users and roles. We use the AddMongoDbStores method of the AspNetCore.Identity.MongoDbCore package (which we installed earlier on the application) to add the MongoDB implementation of Identity. See the highlighted code below:

public void ConfigureServices(IServiceCollection services)
{
    var mongoDbSettings = Configuration.GetSection(nameof(MongoDbConfig)).Get<MongoDbConfig>();

    services.AddIdentity<ApplicationUser, ApplicationRole>()
        .AddMongoDbStores<ApplicationUser, ApplicationRole, Guid>
        (
            mongoDbSettings.ConnectionString, mongoDbSettings.Name
        );

    services.AddControllersWithViews();
}

The third parameter is given as Guid because the primary key of the “Users” and “Roles” Collections are defined as Guid.

With this we have successfully configured Identity with MongoDB database.

Installing MongoDB

You can install MongoDB Community Server on your system and then change the host and port values defined on the appsettings.json file with your MongoDB database.

In this tutorial I will be running MongoDB from Docker. I have also recently completed ASP.NET Core Docker series whose first tutorial is Create first ASP.NET Core App in a Docker Container. If you want to explore Docker then my Docker series will be “really helpful” to you.

There are 2 steps to run MongoDB from Docker:

  1. Install Docker Desktop on your system.
  2. Create a container and run MongoDB from it.

We create MongoDB container creating a file called docker-compose.yml and add the following configurations to it.

version: "3.8"
 
services: 
  mongo:
    image: mongo
    container_name: mongo
    ports:
      - 27017:27017
    volumes: 
      - mongodbdata:/data/db
volumes: 
  mongodbdata:

Next, we open command prompt and navigate to the directory of this file. Then we execute the following command.

docker-compose up -d
docker compose mongodb

This will download the MongoDB image from Docker Hub and create a docker container from where the MongoDB will run.

At this point we have our database running. We can view the MongoDB database and it’s Collections by installing the MongoDB Tools visual studio extension. Install it from Extensions ➤ Manage Extensions menu in your Visual Studio.

MongoDB Tools Extension

Once installed open the Mongo Explorer from Mongo DB Tools ➤ Server Connections menu in VS, here you can see your MongoDB database and it’s Collections.

MongoDB Tools Server Connections

I will come to this part later in the tutorial when I will created Identity Users.

Congratulations, we are now ready to create Identity Users and Roles.

Earlier I wrote tutorials on how to create users and roles in Identity (SQL Server) version. I will be using the same code from these tutorials and will be doing some minor edits. These tutorials are very detailed and informative, you can see these tutorials if you wish to:

Are we missing something? Yes, migrations. You will be pleased to know that we don’t perform migrations in case of MongoDB. The database will be automatically created when we will run the app.

Creating Identity Users in MongoDB

Let us proceed with creating a feature to add Identity Users. So first create a class called User.cs inside the “Models” folder. This class will be used to transfer the form’s data from the View to the Controller. In the view we will have a form where we can fill out the details of a user, and on submitting the form, the application will create the Identity User.

The User.cs code is given below. It has only 3 fields – Name, Email and Password with validation attributes applied to each of them.

using System.ComponentModel.DataAnnotations;

namespace IdentityMongo.Models
{
    public class User
    {
        [Required]
        public string Name { get; set; }

        [Required]
        [EmailAddress(ErrorMessage = "Invalid Email")]
        public string Email { get; set; }

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

Next, create a controller that will add Identity Users in the database. I created a new controller called OperationsController.cs inside the Controllers folder and added the following code to it.

using IdentityMongo.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System;

namespace IdentityMongo.Controllers
{
    public class OperationsController : Controller
    {
        private UserManager<ApplicationUser> userManager;

        public OperationsController(UserManager<ApplicationUser> userManager)
        {
            this.userManager = userManager;
        }

        public ViewResult Create() => View();

        [HttpPost]
        public async Task<IActionResult> Create(User user)
        {
            if (ModelState.IsValid)
            {
                ApplicationUser appUser = new ApplicationUser
                {
                    UserName = user.Name,
                    Email = user.Email
                };

                IdentityResult result = await userManager.CreateAsync(appUser, user.Password);
                if (result.Succeeded)
                    ViewBag.Message = "User Created Successfully";
                else
                {
                    foreach (IdentityError error in result.Errors)
                        ModelState.AddModelError("", error.Description);
                }
            }
            return View(user);
        }
    }
}

The controller is injected with “UserManager” class of type “ApplicationUser” in the constructor. The Create action method simply creates the new Identity User in the database. The UserManager’s CreateAsync method performs the task of creation of the user.

IdentityResult result = await userManager.CreateAsync(appUser, user.Password);

Any errors encountered in the operations are added to the ModelState and shown on the View.

foreach (IdentityError error in result.Errors)
    ModelState.AddModelError("", error.Description);

We now add the Create.cshtml which is a Razor View inside the View ➤ Operations folder. It’s code is given below:

@model User

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

@if (ViewBag.Message != null)
{
    <div class="p-1 mb-2 bg-success text-white">@ViewBag.Message</div>
}
<form method="post">
    <div class="form-group">
        <label asp-for="Name"></label>
        <input asp-for="Name" class="form-control" />
    </div>
    <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 type="submit" class="btn btn-primary">Create</button>
</form>

The View will simply present a form to add Identity User to the MongoDB database. With that said, it’s time to perform the practical. Start the app in Visual Studio and navigate to the url – https://localhost:44323/Operations/Create. The port will be different for your case.

You will be presented with a form to create a user. Fill it will the following values:

Create User View

On submitting the form, you will get the message – User Successfully Created.

User Created

We can verify this user is created in the MongoDB database from Mongo Explorer. So, open it from Mongo DB Tools ➤ Server Connections menu in VS.

Connect to MongoDB Database

Now, click the icon that says “Add new Server Connection”, it will open a new window. Add the database address and port (which will be auto filled). Finally click the OK button and you will see the “Identity” Database is created. See the below image where I have explained this thing.

Mongo Explorer Visual Studio

Recall I said we don’t need to perform Migrations, this is the proof. Let us move one step ahead and create Identity Roles feature in our app.

Creating Identity Roles in MongoDB

We now add the feature to create Identity Roles in MongoDB. For this we need to inject the RoleManager class of type ApplicationRole in the Controller’s constructor. Then by using CreateAsync method of RoleManager, we can create the Roles.

Add a new action method called CreateRole to the OperationsController.cs and make changes to the controller as I have shown by highlighting the codes.

using IdentityMongo.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System;
using System.ComponentModel.DataAnnotations;

namespace IdentityMongo.Controllers
{
    public class OperationsController : Controller
    {
        private UserManager<ApplicationUser> userManager;
        private RoleManager<ApplicationRole> roleManager;

        public OperationsController(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
        {
            this.userManager = userManager;
            this.roleManager = roleManager;
        }

        //..

        public IActionResult CreateRole() => View();

        [HttpPost]
        public async Task<IActionResult> CreateRole([Required] string name)
        {
            if (ModelState.IsValid)
            {
                IdentityResult result = await roleManager.CreateAsync(new ApplicationRole() { Name = name });
                if (result.Succeeded)
                    ViewBag.Message = "Role Created Successfully";
                else
                {
                    foreach (IdentityError error in result.Errors)
                        ModelState.AddModelError("", error.Description);
                }
            }
            return View();
        }
    }
}

Next, add the CreateRole razor view inside the Views ➤ Operations folder with the following code.

@model string

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

@if (ViewBag.Message != null)
{
    <div class="p-1 mb-2 bg-success text-white">@ViewBag.Message</div>
}

<form asp-action="Create" method="post">
    <div class="form-group">
        <label for="name">Name:</label>
        <input name="name" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
</form>

The View will present a small form that only asks for the Name of the Role to be created. Run the app and navigate to the url https://localhost:44323/Operations/CreateRole. Add role name as “Admin” and click the Create button.

Create Identity Role in MongoDB

Let us verify the Role in the database. So, in Mongo Explorer connect to the database once again and you will see “Roles” collections is added. Check the Roles collections to find the “Admin” role. I have shown this in the below image.

Identity Role in Mongo Explorer

User Authentication : ASP.NET Core Identity in MongoDB

Secured areas of the website must be protected from anonymous user access. For this we need to add the authentication and authorization middleware in ASP.NET Core app. Open the Startup class Configure method and add these 2 middlewares after UseRouting.

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

Next, add a new controller called SecuredController.cs in your project and secure it from anonymous user access by adding [Authorize] attribute over it.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace IdentityMongo.Controllers
{
    [Authorize]
    public class SecuredController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

The controller just has a single action “Index”. The Index view will show the authenticated user Claims and Properties. So add this index view inside the Views ➤ Operations folder with the following code.

@using Microsoft.AspNetCore.Authentication

<h2>Claims</h2>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

<h2>Properties</h2>

<dl>
    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
    {
        <dt>@prop.Key</dt>
        <dd>@prop.Value</dd>
    }
</dl>

Before creating the Login feature, I will like to discuss how this authentication work. When an anonymous user tries to visit the Secured Controller, the following things will happen:

  1. User will be redirected to the Login page.
  2. User enters his credentials and clicks the log in button.
  3. On successful login user will be redirected to the secured controller and the secured information will be shown to him.

Theses things are shown in the below video, check them carefully.

Identity Authentication Video
Login and Logout feature

The user must also have the option to login and logout to the website. Let’s create this feature in a new controller called “AccountController.cs”.

We will need to inject UserManager and SignInManager classes of type ApplicationUser to the constructor. SignInManager class is used to sign in an application.

using IdentityMongo.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;

namespace IdentityMongo.Controllers
{
    public class AccountController : Controller
    {
        private UserManager<ApplicationUser> userManager;
        private SignInManager<ApplicationUser> signInManager;

        public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
        {
            this.userManager = userManager;
            this.signInManager = signInManager;
        }
        
        // login and logout actions 
    }
}

Next add 2 actions “Login” and “Logout” to the controller.

  • Login
  • Logout

The login action will be used when user want’s to log in to the application.

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

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

    return View();
}

Look at the above code, we first find the user in the database from UserManager class.

ApplicationUser appUser = await userManager.FindByEmailAsync(email);

Next, we use SignInManager class to login in the Application User.

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

We will also need to create Login.cshtml file which will serve as the Razor View for Login action. Add this file inside the Views ➤ Account folder (code is shown below). The login view will show Email and Password text boxes, which the user has to fill in, in order to sign in to the application.

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

<form method="post">
    <div class="form-group">
        <label>Email</label>
        <input name="email" class="form-control" />
    </div>
    <div class="form-group">
        <label>Password</label>
        <input name="password" class="form-control" />
    </div>
    <button class="btn btn-primary" type="submit">Log In</button>
</form>

Last but not least add the Logout action to the controller. This action has been applied with [Authorize] attribute as only the logged in users can initiate this action.

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

Show Logged in User Name and Logout button

The best place to show the username of logged in user along with the logout option, is the header section of the website. I add them to the _Layout.cshtml file as shown below:

<ul class="navbar-nav flex-grow-1">
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
    </li>
    @{
        if (User?.Identity?.IsAuthenticated ?? false)
        {
            <li class="nav-item">
                <u>Welcome: @User.Identity.Name</u>
                <a asp-controller="Account" asp-action="Logout" class="btn btn-danger">Logout</a>
            </li>

        }
    }
</ul>

In the if condition we check the claims identity property “IsAuthenticated” to find out if the user is authenticated or not. If he is indeed authentication, then only his name and logout buttons are shown.

if (User?.Identity?.IsAuthenticated ?? false)
{
    // executes only when the user is authenticated
}
Testing Authentication feature

Run the app in visual studio and then navigate to the url – https://localhost:44323/Account/Login. You will be shown the Login form.

Login Form

Login with the user we created earlier.

Next, go to the url – https://localhost:44323/Secured where you will see your claims and properties. Also notice your username and logout button at the header area.

Secured Controller by Identity

Authentication by Roles

We can also put a condition on the controller so that it can only be accessed by users in a given role. For example – we can say only users in “Admin” roles are allowed to view contents of the app. This is known as authentication by roles.

Change the Secured Controller authorize attribute to include only users of Admin role – [Authorize(Roles = “Admin”)].

[Authorize(Roles = "Admin")]
public class SecuredController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Now run the app and navigate to https://localhost:44323/Secured. You will get redirected to login page, login with the same user we created earlier.

After login you will not reach the Secured controller instead redirected to access denied page whose url is “/Account/AccessDenied”.

Access Denied by Identity

We haven’t created the AccessDenied action on the Account controller so we are getting 404 error. This thing you can do by yourself. Anyway, the point is the logged in user is not in the “Admin” role so he can’t be authorized to view the Secured Controller.

Let us do a slight modification to the “Create” action of the “OperationsController.cs” so the new users are automatically added to “Admin” role. You just need to add a single line to it which I have shown in highlighted color.

[HttpPost]
public async Task<IActionResult> Create(User user)
{
    if (ModelState.IsValid)
    {
        ApplicationUser appUser = new ApplicationUser
        {
            UserName = user.Name,
            Email = user.Email
        };

        IdentityResult result = await userManager.CreateAsync(appUser, user.Password);

        //Adding User to Admin Role
        await userManager.AddToRoleAsync(appUser, "Admin");

        if (result.Succeeded)
            ViewBag.Message = "User Created Successfully";
        else
        {
            foreach (IdentityError error in result.Errors)
                ModelState.AddModelError("", error.Description);
        }
    }
    return View(user);
}

Now create a new user which will be added to “Admin” role. Next login with this new user and navigate to the url of the secured user. You will now be authorized to access the controller.

Note that more than one roles can be added to the [Authorize] attribute in comma separated manner.

[Authorize(Roles = "Admin, Teacher, Developer")]
I haven’t created a full feature where we can add/remove Identity Users to and from a Role. This would have un-necessary deviated this MongoDB Identity Tutorial from the main course. However, I would like to tell you that I have created this feature in my other tutorial whose link is Adding and Removing Users from Identity Roles. You can copy the codes from there and add it to this tutorial codes (plus some minor edits).
Conclusion

In this tutorial we applied MongoDB for Identity database, the users and roles were stored in the MongoDB database. We also created login and logout feature and then authenticated users to secured controllers along with their roles. If you like this tutorial kindly share it by using the below social buttons.

SHARE THIS ARTICLE

  • linkedin
  • reddit
yogihosting

ABOUT THE AUTHOR

I hope you enjoyed reading this tutorial. If it helped you then consider buying a cup of coffee for me. This will help me in writing more such good tutorials for the readers. Thank you. Buy Me A Coffee donate

Leave a Reply

Your email address will not be published. Required fields are marked *