First of all, thank Master Xiaochen and EdisonChou for reviewing the manuscript! Thank you for reading!

The introduction

In general, the resources and apis exposed by a service must be accessible only to certain trusted users and clients. The first step in making apI-level trust decisions is authentication — determining whether a user’s identity is reliable.

In microservice scenarios, identity authentication is generally handled uniformly. There are generally two implementations:

  1. API gateway based centralized authentication: The client must access the microservice through the gateway. (This requires a security mechanism to verify that the request is coming from the gateway.)

  2. Authentication based on Security Token Service (STS): All clients obtain the token from the STS and then carry the token with them when requesting authentication.

The Identity MicroService described in this section uses the second authentication method.

Service profile

Identity MicroService is used for unified Identity authentication and authorization and provides support for other services.

When it comes to authentication, we are most familiar with Cookie authentication, which is the most widely used authentication method at present. However, Cookie authentication also has its limitations: it does not support cross-domain and is not friendly to mobile terminals. However, the current architecture needs to support cross-authentication and authorization between mobile terminal, Web terminal and micro-service, so the traditional cookie-based local authentication scheme is not feasible. We need to use remote authentication to provide a unified authentication and authorization mechanism.

Remote authentication should be OAuth2.0 and OpenID Connect. With the help of OAuth2.0 and OpenID Connect, an authentication system similar to the following can be implemented:

And how to do that, with:

  1. ASP.NET Core Identity
  2. IdentityServer4

The differences between cookie-based authentication and token-based authentication are as follows:

Architectural patterns

As a support service, this micro-service does not choose complex architecture mode, uses MVC single-layer architecture, uses EF Core ORM framework for data persistence, and uses SQL Server database. The default dependency injection framework was replaced with the Autofac IOC framework.

The project structure is as follows:

Selection of core technology:

  1. MVC single layer architecture
  2. EF Core
  3. ASP.NET Core Identity
  4. IdentityServer4
  5. SQL Server database
  6. Autofac

ASP.NET Core Identity, Identity server 4, and OAuth2.0

ASP.NET Core Identity and Identity server4 are used in this service.

ASP.NET Core Identity && IdentityServer4

ASP.NET Core Identity is used to build the membership system for ASP.NET Core Web applications, including membership, login, and user data (including login information, roles, and declarations). ASP.NET Core Identity encapsulates User, Role, Claim and other Identity information, which is convenient for us to quickly complete the implementation of the login function, and supports third-party login (Google, Facebook, QQ, Weixin, etc., support out of the box [third-party Identity provider list]). And two-factor validation with built-in support for Bearer authentication.

While ASP.NET Core Identity already does most of the work and supports third-party logins (who issue tokens to their users), to issue tokens to local users, you need to implement your own token issuance and validation logic. In other words, we need to implement the OpenId Connect protocol ourselves.

OpenID Connect 1.0 is a simple identity layer based on the OAuth 2.0 protocol, which allows the client to confirm the identity of the end user and obtain basic user information based on the authentication results of the authorized server.

IdentityServer4 is the authentication and authorization middleware that implements OpenId Connect and OAuth2.0 protocols for ASP.NET Core. ASP.NET Core Identity provides authentication for token issuance.

Introduction to the Certification Process

ASP.NET Core uses claim-based authentication. What is a Cliam?

A Claim is a statement about something about a person or organization, such as: a person’s name, role, preferences, race, privilege, community, abilities, etc. It is essentially a key-value pair, a very generic way to store user information. It is easy to separate authentication from authorization, the former being used to indicate what the user is/is not, and the latter being used to indicate what the user can/cannot do. During the authentication phase, we obtain Claims from users through user information, and authorization is used to verify these Claims, such as whether the user has the role of Admin, whether the name is XXX, etc.

Authentication deals with the following core objects:

  1. Claim information
  2. ClaimsIdentity
  3. ClaimsPrincipal identity Card Holder
  4. AuthorizationToken
  5. IAuthenticationScheme
  6. IAuthenticationHandler (authentication handler corresponding to the authentication scheme)
  7. IAuthenticationService (provides a unified authentication service interface externally)

What is the certification process?

The User opens the login interface, enters the User name and password to log in first, and the server verifies whether the User name and password are valid. If the User instance is valid, the server returns the User instance, and then enters the authentication preparation stage. The ClaimsIdentity is created according to the Claim information carried by the User instance. The identity card is then handed over to the ClaimsPrincipal holder. Then enter the real authentication phase, according to the configured authentication Scheme (IAuthenticationHandler), using the corresponding authentication handler for authentication. After the authentication is successful, an AuthorizationToken is issued. This authorization token contains all the information required for subsequent authorization phases.

Introduction to the Authorization Process

An entitlement is an assertion of Claims made by a user:

  1. Role-based authorization
  2. Scheme-based authorization
  3. Policy-based authorization

Authorization deals with the following core objects:

  1. IAuthorizationRequirement (authorization conditions)
  2. IAuthorizationService (AuthorizationService)
  3. AuthorizationPolicy
  4. IAuthorizationHandler (Authorization handler)
  5. AuthorizationResult

What about the authorization process?

When I received the authorization request, the authorization service (IAuthorizationService) according to the resources specified authorization policy (AuthorizationPolicy) contained in the terms of authorization (IAuthorizationRequirement), Find the corresponding authorization handler (IAuthorizationHandler) to determine whether the identity information contained in the authorization token meets the authorization conditions, and return the authorization result.

Middleware integration

After a brief look at the authentication and authorization process, let’s look at how Identity MicroService integrates with related middleware.

1. Map the users and roles of custom extensions first

// Map custom User, Role services.AddIdentity<ApplicationUser, IdentityRole > (.) AddEntityFrameworkStores < ApplicationDbContext > () / / configuration using EF persistent storage. AddDefaultTokenProviders (); // Configure the default TokenProvider to generate tokens when changing passwords and email addressesCopy the code

2. Configure IdentityServer

// Adds IdentityServer
services.AddIdentityServer(x =>
{
    x.IssuerUri = "null";
    x.Authentication.CookieLifetime = TimeSpan.FromHours(2);
})
.AddSigningCredential(Certificate.Get())
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
    options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
     sqlServerOptionsAction: sqlOptions =>
     {
         sqlOptions.MigrationsAssembly(migrationsAssembly);
         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency 
         sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
     });
})
.AddOperationalStore(options =>
{
    options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
     sqlServerOptionsAction: sqlOptions =>
     {
         sqlOptions.MigrationsAssembly(migrationsAssembly);
         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency 
         sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
     });
})
.Services.AddTransient<IProfileService, ProfileService>();Copy the code

By default, IdentityServer stores configuration data (client and resource) and operational data (token, code, and user authorization information consents) directly in memory. This is obviously not appropriate in a production environment, where if the host hosting the service goes down, the data in memory will be lost, so persistence to the database is necessary. The AddConfigurationStore and AddOperationalStore extension methods are used to specify configuration data and operation data to persist based on EF.

3. Add IdentityServer middleware

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
     // .....
    // Adds IdentityServer
    app.UseIdentityServer();
}Copy the code

4. Preset seed data

From a given architecture, we need to preset Client and Resource:

  1. Client
public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl) { return new List<Client> { // SPA OpenId Client Client(Implicit) new Client // Xamarin Client(Hybrid) new Client // MVC Client(Hybrid) new Client // MVC TEST Client(Hybrid) new Client // Locations Swagger UI(Implicit) new Client // Marketing Swagger UI(Implicit) new Client  // Basket Swagger UI(Implicit) new Client // Ordering Swagger UI(Implicit) new Client // Mobile Shopping Aggregattor Swagger UI(Implicit) new Client // Web Shopping Aggregattor Swagger UI(Implicit) new Client }; }Copy the code
  1. IdentityResources
public static IEnumerable<IdentityResource> GetResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };
}Copy the code
  1. ApiResources
public static IEnumerable<ApiResource> GetApis()
{
    return new List<ApiResource>
    {
        new ApiResource("orders", "Orders Service"),
        new ApiResource("basket", "Basket Service"),
        new ApiResource("marketing", "Marketing Service"),
        new ApiResource("locations", "Locations Service"),
        new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"),
        new ApiResource("webshoppingagg", "Web Shopping Aggregator"),
        new ApiResource("orders.signalrhub", "Ordering Signalr Hub")
    };
}Copy the code

5. Migrate the database context

Now, how do we migrate the seed data preset in the code to the database? IdentityServer defines DBContext for persistent configuration data for ConfigurationDbContext and PersistedGrantDbContext for operation data. The code looks like this:

public static void Main(string[] args) { BuildWebHost(args) .MigrateDbContext<PersistedGrantDbContext>((_, MigrateDbContext<ApplicationDbContext>((context, services) => { var env = services.GetService<IHostingEnvironment>(); var logger = services.GetService<ILogger<ApplicationDbContextSeed>>(); var settings = services.GetService<IOptions<AppSettings>>(); new ApplicationDbContextSeed() .SeedAsync(context, env, logger, settings) .Wait(); MigrateDbContext<ConfigurationDbContext>((Context,services)=> {var Configuration = services.GetService<IConfiguration>(); new ConfigurationDbContextSeed() .SeedAsync(context, configuration) .Wait(); })// Migrate configuration database.run (); }Copy the code

At this point, the core code of the service has been resolved.

The resulting database is shown below:

The last

This paper analyzes the service from the business and technology, introduces its technology selection, and then briefly introduces ASP.NET Core Identity and IdentityServer4, finally analyzes the source code, step by step to uncover its mysterious veil. I’ll cover how clients and other microservices use Identity MicroService for authentication and authorization in a future article.

ASP.NET Core Idenity and IdentityServer4 I believe that your implementation mechanism of Identity MicroService has suddenly become clear.