Skip to content

Instantly share code, notes, and snippets.

@jessenich
Last active April 19, 2021 07:05
Show Gist options
  • Select an option

  • Save jessenich/096305fa05d1494b1dca6ae4b554b60b to your computer and use it in GitHub Desktop.

Select an option

Save jessenich/096305fa05d1494b1dca6ae4b554b60b to your computer and use it in GitHub Desktop.
### Gists somehow lost their proper order, follow by section #'s in the headers.

Section 2: Adding the NuGet packages

Enough with the theory, let's put the plan into action.

From Solution Explorer, right-click on the WorldCities tree node, then select Manage NuGet Packages, look for the following two packages, and install them:

  • Microsoft.AspNetCore.Identity.EntityFrameworkCore
  • Microsoft.AspNetCore.ApiAuthorization.IdentityServer

Alternatively, open the Package Manager Console and install them with the following commands:

Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore Install-Package Microsoft.AspNetCore.ApiAuthorization.IdentityServer

Section 5: Configuring the ASP.NET Core Identity middleware

Now that we're done with all the prerequisites, we can open the Startup.cs file and add the following highlighted lines in the ConfigureServices method to set up the middleware required by the ASP.NET Core Identity system:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews()
        .AddJsonOptions(options => {
            // set this option to TRUE to indent the JSON output
            options.JsonSerializerOptions.WriteIndented = true;
            // set this option to NULL to use PascalCase instead of
            // CamelCase (default)
            // options.JsonSerializerOptions.PropertyNamingPolicy = null;
        });
        
    // In production, the Angular files will be served from
    // this directory
    services.AddSpaStaticFiles(configuration =>
    {
        configuration.RootPath = "ClientApp/dist";
    });
    
    // Add EntityFramework support for SqlServer.
    services.AddEntityFrameworkSqlServer();
    
    // Add ApplicationDbContext.
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")
    ));
    
    // Add ASP.NET Core Identity support
    var defaultIdentity = services.AddDefaultIdentity<ApplicationUser>(options =>
    {
        options.SignIn.RequireConfirmedAccount = true;
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireUppercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequiredLength = 8;
    })
    
    defaultIdentity.AddRoles<IdentityRole>()
      .AddEntityFrameworkStores<ApplicationDbContext>();
    
    services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
        
    services.AddAuthentication()
        .AddIdentityServerJwt();
}

In the Configure method, add the following highlighted lines:

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

The preceding code strictly resembles the default .NET Core Identity implementation for SPA projects. If we created a new ASP.NET Core web application using the Visual Studio wizard, selecting the Individual User Accounts authentication method (see the following screenshot), we would end up with the same code, with some minor differences.

It's worth noting that the preceding code will require a reference to the new identity related packages that we installed a moment ago:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;

Now that we have properly configured our Setup class, we need to do the same with IdentityServer.

Section 3: Creating ApplicationUser

Now that we have installed the required identity libraries, we need to create a new ApplicationUser entity class with all the features required by the ASP.NET Core Identity service to use it for auth purposes. Luckily enough, the package comes with a built-in IdentityUser base class that can be used to extend our own implementation, thus granting it all that we need.

From Solution Explorer, navigate to the /Data/Models/ folder, then create a new ApplicationUser.cs class and fill its content with the following code:

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

namespace WorldCities.Data.Models
{
    public class ApplicationUser : IdentityUser
    {
    }
}

As we can see, we don't need to implement anything there, at least for the time being: we'll just extend the IdentityUser base class, which already contains everything we need for now.

Section 4: Extending ApplicationDbContext

In order to support the .NET Core authentication mechanism, our existing ApplicationDbContext needs to be extended from a different database abstraction base class that supports ASP.NET Core Identity and IdentityServer. Open the /Data/ApplicationDbContext.cs file and update its contents accordingly.

// Change #1: Add using statement for IS4 namespace.
using IdentityServer4.EntityFramework.Options;

// Change #2 Add using statement for ASP.NET's "bridging" library between 
// ASP.NET Core Identity and IS4
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using WorldCities.Data.Models;
namespace WorldCities.Data 
{
    // Change 3: Inherit from 'ApiAuthorizationDbContext<ApplicationUser>'
    public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
    {

        // Change #4: Add 'OperationalStoreOptions' from IS4 to primary constructor, pass to newly inherited base class.
        public ApplicationDbContext(DbContextOptions dbCtxOpts, IOptions<OperationalStoreOptions> opStoreOpts) 
            : base(dbCxOpts, opStoreOpts)
        { }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            // Map Entity names to DB Table names
            modelBuilder.Entity<City>().ToTable("Cities");
            modelBuilder.Entity<Country>().ToTable("Countries");
        }
    
        public DbSet<City> Cities { get; set; }
        public DbSet<Country> Countries { get; set; }
    }
}

As we can see from the preceding code, we changed the current DbContext base class with the new ApiAuthorizationDbContext base class; the new class strongly relies on the IdentityServer middleware, which also required a change in the constructor signature to accept some options that are required for properly configuring the operational context.

For additional information about the .NET authentication and authorization system for SPA, ASP.NET Core Identity API, and the .NET Core IdentityServer, check out the following URL:

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization

Section1: Setting up ASP.NET Core Identity

When we created our HealthCheck and WorldCities .NET Core projects, we always made the choice to go with an empty project featuring no authentication. That was because we didn't want Visual Studio to install ASP.NET Core Identity within our application's startup files right from the start. However, now that we'll use it, we need to manually perform the required setup steps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment