Hands-On ASP.NET Core 3.1 : Learn MVC, Razor Pages, Web API, Entity Framework Core, and Blazor. Private online coaching for software developers. Click here to know more.

Integrate IdentityServer with ASP.NET Core (Part 6 - Users in Db)

In Part - 5 of this series you learned to stored IdentityServer configuration data such as identity resources, API resources, and clients into a SQL Server database. However, our sample application still uses TestUser objects to sign into the system. As you would have guessed, in a more real-world app you would like to rope in ASP.NET Core Identity to perform membership tasks. In such a case, instead of using TestUsers you would have IdentityUsers (or its subclass). Let's see how this can be accomplished in this concluding part of this series.

To keep things simple and focused we won't touch the IdentityServerDemo.Server project for this example. We will create a new ASP.NET Core web app that uses ASP.NET Core Identity based authentication. Let's name this project IdentityServerDemo.Server.AspNetIdentity.

Change the authentication scheme to Individual User Accounts as shown below:

The newly created ASP.NET Core MVC project uses ASP.NET Core Identity and stores user data in LocalDb database. We will change the database to Northwind (or whatever database you used earlier). To do so open the appsettings.json file and change the database connection string as shown below:

"ConnectionStrings": {
    "AppDb": "data source=.;initial catalog=
Northwind;Integrated Security=true"
  }

You will notice that the newly created project already contains Data > Migrations folder.

You can also see a custom DbContext class named ApplicationDbContext automatically created for you.

Now we will create tables required by ASP.NET Core Identity into the Northwind database. Open Visual Studio developer command prompt and go inside the newly created project's folder. Then issue the following CLI command :

> dotnet ef database update -context ApplicationDbContext

Once this command completes successfully, you will see the following tables in the Northwind database :

Next, go to the Startup file and observe the code inside ConfigureServices() method.

public void ConfigureServices(IServiceCollection services)
{

    string connStr = Configuration.GetConnectionString("AppDb");

    services.AddDbContext<ApplicationDbContext>
(options => options.UseSqlServer(connStr));

    services.AddDefaultIdentity<IdentityUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddControllersWithViews();
    services.AddRazorPages();
}

As you can see, the AddDbContext() call registers ApplicationDbContext with the DI container. Similarly calls to AddDefaultIdentity() and AddEntityFrameworkStores() register the required ASP.NET Core Identity services with the framework. I won't go into the details of these configuration settings here. You may consider reading the official documentation.

At this stage the newly created project doesn't contain any IdentityServer related code but you can go ahead and create couple of user accounts.

To do so, run the newly created application and click on the Register link at the top.

Note that the default configuration of ASP.NET Core Identity expects an email as a user name. Supply some user name and password and click on the Register button to create an account.

Once the accounts are created, click on the Login link at the top and try signing in to the system by entering a valid user ID and password.

If all goes well you will see a welcome message at the top.

Now that our application is having a few ASP.NET Core Identity users we will add IdentityServer configuration to the project. For the sake of simplicity we will use in-memory configuration. But later you can switch to database based configuration as described in Part - 5 of this series.

Add the IdentityServer4.AspNetIdentity NuGet packages to the project.

So, copy the ServerConfiguration.cs file from earlier parts of this series into the newly created project (depending on your project setup you might need to adjust its namespace). And then modify the ConfigureServices() as shown below:

public void ConfigureServices(IServiceCollection services)
{
    string connStr = Configuration.
GetConnectionString("AppDb");

    services.AddDbContext<ApplicationDbContext>
(options => options.UseSqlServer(connStr));
    services.AddDefaultIdentity<IdentityUser>()
        .AddEntityFrameworkStores
<ApplicationDbContext>();

    services.AddControllersWithViews();
    services.AddRazorPages();


    services.AddIdentityServer()
            .AddInMemoryApiResources
(ServerConfiguration.ApiResources)
            .AddInMemoryApiScopes
(ServerConfiguration.ApiScopes)
            .AddInMemoryIdentityResources
(ServerConfiguration.IdentityResources)
            .AddInMemoryClients
(ServerConfiguration.Clients)
            .AddDeveloperSigningCredential()
            .AddAspNetIdentity<IdentityUser>();
}

Notice that AddIdentityServer() call is placed after the AddIdentity() call. Also notice that instead of AddTestUsers() call we now use AddAspNetIdentity() call at the end of the chain. Since we are using the default configuration of ASP.NET Core Identity, a user is represented by IdentityUser class.

Now wire IdentityServer middleware in the Configure() method.

public void Configure(IApplicationBuilder app, 
IWebHostEnvironment env)
{
    ...
    app.UseStaticFiles();
    app.UseRouting();
    app.UseIdentityServer();
    app.UseAuthentication();
    app.UseAuthorization();
    ...
}

Be sure to insert it between UseRouting() amd UseAuthentication().

You need to make a small change to the Client2 configuration from ServerConfiguration.cs file. Go to Client2 and set the RequireConsent property to false.

public static List<Client> Clients
{
    get
    {
        Client client1 = new Client
        {
           ...
        };


        Client client2 = new Client
        {
            ClientName = "Client 2",
            ClientId = "client2",
            ...
            RequireConsent = false
        };

        List<Client> clients = new List<Client>();
        clients.Add(client1);
        clients.Add(client2);

        return clients;
    }
}

We need this modification because we are using ASP.NET Core Identity in its simplistic form and don't have consent page ready (the IdentityServer QuickStart comes with a consent page. So, our earlier projects worked even when RequireConsent was set to true).

Now, open the IdentityServerDemo.Client project and change the [Authorize] attribute added on top of HomeController like this :

[Authorize]
public class HomeController : Controller
{
}

Here, we neither use roles nor policies because ASP.NET Core Identity database doesn't have that information. of course, you can see that data later to again switch to role or policy based authorization. To keep things simple we use just the simple authentication here.

You need to make this change in the IdentityServerDemo.WebApi project also.

[Authorize]
public class EmployeesController : ControllerBase
{
  [HttpGet]
  public List<string> Get()
  {
    return new List<string>() { 
        "Nancy Davolio",
        "Andrew Fuller",
        "Janet Leverling"
  };
}

Notice that we have also removed user claims related code that we added in Part - 4. That is because default IdentityUser doesn't have properties to hold given_name, family_name, phone, and address. Of course, you can create a custom user class that has these properties. Here, we will stick to the bare minimum IdentityUser class.

This completes the configuration. Now you are ready to run all the three projects.

First, run the IdentityServerDemo.Server.AspNetIdentity project. Observe that this project runs on HTTP port 5000 (and HTTPS port 5001). This is the same port where IdentityServerDemo.Server project used to run. Then run IdentityServerDemo.WebApi project. Finally, run the IdentityServerDemo.Client project.

As soon as you run the client application, you will be shown the login page. Log in with user1@localhost (or whatever email you used during registration step). You will now see the employee names as shown below:

In the previous part IdentityServer configuration data was stored in a database and user data was in-memory. In this part user data is stored in a database and IdentityServer configuration is stored in-memory. But you can combine what you learned in Part 5 and Part 6 easily so that all the data (configuration and user) is stored in a database.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant, trainer, author, yoga mentor, and meditation teacher. He has been programming, meditating, and teaching for 24+ years. He conducts instructor-led online training courses in ASP.NET family of technologies for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Facebook  Twitter  LinkedIn  YouTube

Posted On : 07 September 2020


Tags : ASP.NET ASP.NET Core Data Access .NET Framework C# Visual Studio


Subscribe to our newsletter

Get monthly email updates about new articles, tutorials, code samples, and how-tos getting added to our knowledge base.

  

Receive Weekly Updates