Skip to content

Startup code

Jon P Smith edited this page Aug 13, 2023 · 27 revisions

Like all ASP.NET Core libraries the AuthP library has to be registered as a service. Also, yhe AuthP library follows the ASP.NET Core of format of configuring itself with extension methods, and providing configuration points.

Here is a simple example of what registering the AuthP library would look like, but there are lots of different extension methods and configurations.

services.RegisterAuthPermissions<YourPermissions>(options =>
    {
        options.PathToFolderToLock = _env.WebRootPath;
    })
    .UsingEfCoreSqlServer(Configuration.GetConnectionString("DefaultConnection")) 
    .IndividualAccountsAuthentication()
    .RegisterAuthenticationProviderReader<SyncIndividualAccountUsers>()
    .IndividualAccountsAddSuperUser()
    .SetupAspNetCoreAndDatabase();

A list of the AuthP's extensions and what you can use

The AuthP library has lots of options and the table below shows the various groups and when and where they should go.

Group Method Needed?
Configure RegisterAuthPermissions<TEnum>
RegisterTenantChangeService
Required
Authentication IndividualAccountsAuthentication
AzureAdAuthentication
ManualSetupOfAuthentication
Required, second
Database UsingEfCoreSqlServer
UsingEfCorePostgres
UsingInMemoryDatabase
Only one required
Sharding SetupMultiTenantSharding Optional
Extra features RegisterAddClaimToUser
IRegisterStateChangeEvent
ReplaceShardingConnections
SetupAuthPLocalization
Optional
Bulk Load AddTenantsIfEmpty
AddRolesPermissionsIfEmpty
AddAuthUsersIfEmpty
RegisterFindUserInfoService<TLookup>
Optional
User Admin RegisterAuthenticationProviderReader<TSync> Required for admin
SuperUser AddSuperUserToIndividualAccounts Optional
Finalize SetupAspNetCoreAndDatabase
SetupAspNetCorePart
SetupForUnitTestingAsync
One required, last

The following sections explains each Group in more detail.

Configure

1. RegisterAuthPermissions

You MUST start with the RegisterAuthPermissions<TEnum> method. This starts the registration of AuthP. There are two parts to this configuration method.

  1. It allows you to tell AuthP what enum contains your Permissions.
    NOTE: This method will throw an exception you Permissions the enum has the : ushort applied to it.
  2. You can set the AuthP's options, i.e. setting parameters in the AuthPermissionsOptions class.

Here is the Example3 usage which defines the Example3Permissions Permissions and sets some options.

services.RegisterAuthPermissions<Example3Permissions>(options =>
   {
       //This define a single-level multi-tenant app
       options.TenantType = TenantTypes.SingleLevel; 
       //This turns on an admin feature allowing app users to access a tenant's data
       options.LinkToTenantType = LinkToTenantTypes.OnlyAppUsers; 
       //sets EncryptionKey used in "invite user" feature 
       options.EncryptionKey = _configuration[nameof(AuthPermissionsOptions.EncryptionKey)];
       //Provides a secondary global resource to lock when migrating on startup
       options.PathToFolderToLock = _env.WebRootPath; 
   })
   // ... rest of the setup left out 

2. RegisterTenantChangeService

If you are building a multi-tenant application, then you need to build an implementation if the ITenantChangeService interface (see Building a tenant change service) and then register that implementation via the RegisterTenantChangeService<T> extension method - see example below

.RegisterTenantChangeService<ShardingOnlyTenantChangeService>()

Authentication

The AuthP library relies on the ASP.NET Core authentication handlers for managing the registration and login of a user. To use an ASP.NET Core authentication handler you need to connect to the authentication handler's login, so that the AuthP can add claims containing the AuthP permissions and if your application is multi-tenant, then it will also provide a DataKey for users linked to a AuthP tenant.

The AuthP library currently has support for:

NOTE: A more detailed description of how to link to an ASP.NET Core authentication handler can be found in the Setup Authentication document.

But if you want to use a different ASP.NET Core authentication handler, then you need to create a link into the authentication handler's login process, which is described in the this section in the article called "Advanced techniques around ASP.NET Core Users and their claims".

Database

You need to select a database type where AuthP can store its information. At the moment there are only two options:

1. UsingEfCoreSqlServer

This method takes in a connection string to a SQL Server database where the AuthP entities are stored.

NOTE: The AuthP code is designed to be share a database that other parts of the application uses, e.g. your own EF Core database, the Individual Account database, and so on. This works because the AuthP's database has its own named EF Core migration history table so that it can be migrated without effecting other parts of the database. This reduces the number databases your application needs.

2. UsingEfCorePostgres

This method takes in a connection string to a Postgres database where the AuthP entities are stored.

NOTE: Like the UsingEfCoreSqlServer version, the Postgres database can be shared with tenat data.

3. UsingInMemoryDatabase

This creates a SQLite in-memory database. On startup the database will be created, and when the application closed the database will be disposed.

An in-memory database is there mainly for unit testing your application. The Bulk load methods are also useful for seeding the database at the start of a unit test.

Sharding

The SetupMultiTenantSharding extension method will setup the sharding / hybrid settings. NOTE: You still need define which options.TenantType to either the single-level or hierarchical multi-tenant setting.

Extra features

There several options that provide extra features when using the AuthP library

1. RegisterAddClaimToUser

You can add extra claim(s) to a user by added the RegisterAddClaimToUser<TClaimsAdder> extension method when registering the AuthP library. The RegisterAddClaimToUser<TClaimsAdder> method registers your claim builder service, which must implement the IClaimsAdder interface, to the .NET DI. Whenever AuthP's ClaimsCalculator is called your registered claim builder(s) will be run and any claims will be added to the logged-in user.

NOTE: See sections 2 and 3 in the article called to explain why and how you might add extra claims to your application.

Here is the registering of two claims in the Example3 application.

services.RegisterAuthPermissions<Example3Permissions>(options =>
   //... other parts left out
   .RegisterAddClaimToUser<AddTenantNameClaim>()
   .RegisterAddClaimToUser<AddRefreshEveryMinuteClaim>()
   //... etc.

NOTE: Be aware - if you register your claim builder using the RegisterAddClaimToUser method, then you must ensure the service isn't registered to .NET DI in your other code. If you do then you will add two claims with the same name, which can cause problems (and is inefficient too).

2. IRegisterStateChangeEvent

Version 2.3.0 added the ability to register service that can use EF Core's events to detect a change to the AuthP database. This allows to trigger other code on such a change. In Example4 application a "detect change" service called RegisterTenantDataKeyChangeService which detect a change to a DataKey. You can read why and how this is useful in this section of the article called "Advanced techniques around ASP.NET Core Users and their claims".

NOTE: There is no AuthP extension method to register your "detect change" service, so you need to manually register the service to the .NET DI provider.

3. ReplaceShardingConnections<T>

When using AuthP's sharding feature a service called ShardingConnections is used to create the correct connection strings for the different databases.

You might want to alter this code for some reason, e.g. supporting other types of databases such as MySQL. In this case you can create your own implementation of the IShardingConnections interface and use the ReplaceShardingConnections<T> method, e.g.

services.RegisterAuthPermissions<MyPermissions>(
{
    options.TenantType = TenantTypes.SingleLevel | TenantTypes.AddSharding;
}
    .ReplaceShardingConnections<MyDifferentSharingService>()
    //... other registration methods left out

4. SetupAuthPLocalization<TResource>

This sets up two parts that that the AuthP localization system needs to work. They are:

  • The <TResource> class provides part of the resource files' name and is required. See more about this in the Usage -> supporting multiple languages section.
  • The supportedCultures parameter should be set to an array of culture names your app supports, e.g new[] { "en", "fr" }. This list can usually come from your code to register the .NET localization service. The main use of this is control the warning logged if a resource file/lookup fails on supported cultures. This array is also added to the AuthPermissionsOptions.InternalData.SupportedCultures, which can be useful in some places - In the Example1 web app I used this to offer a list of the supported cultures for the user to select. There are two non-standard settings you can apply to the supportedCultures parameter:
    • You supply an empty array, e.g. Array.Empty<string>(). This means no warnings will be logged for fail lookups.
    • You supply a null. This will log a ANY culture that either failed to lookup the key, or that culture isn't supported. This might get you some useful data on your users, but you might also get a LOT of logs!

Bulk Load

The bulk load methods allow you to setup Role, Multi-tenants, and AuthUsers, but only if the database has no Roles, Multi-tenants or AuthUsers respectively. This provides a quick way to load AuthP settings on startup. That's mainly when unit testing, but it is be helpful when adding AuthP to an existing application.

The Bulk Load features are also available as an admin service for cases where an admin user wants to load a large number of Roles, Multi-tenants or AuthUsers.

There are three Bulk Load extension methods which will update the AuthP's database on application startup. They are:

  • AddTenantsIfEmpty: Adds AuthP multi-tenant Tenants if no Tenants already exists.
  • AddRolesPermissionsIfEmpty : Adds AuthP Roles if no Roles already exists.
  • AddAuthUsersIfEmpty: Adds AuthP's Users if no AuthUsers already exist.

The other method is the RegisterFindUserInfoService<TLookup>, which allows you to provide class which implements the IFindUserInfoService interface. Your class is registered to the service provider and is used by the Bulk Load (setup or admin services) to obtain the UserId of a authentication provider user via either the user's Email address, or their UserName.

The code below provides an example of how bulk load features can be added to the registering of AuthP in ASP.NET Core's ConfigureServices method in the Startup class. The code was taken from Example4's startup class, which has a good example of using all three startup bulk load extension methods.

services.RegisterAuthPermissions<Example4Permissions>()
    .AddRolesPermissionsIfEmpty(Example4AppAuthSetupData.BulkLoadRolesWithPermissions)
    .AddTenantsIfEmpty(Example4AppAuthSetupData.BulkHierarchicalTenants)
    .AddAuthUsersIfEmpty(Example4AppAuthSetupData.UsersRolesDefinition)
    .RegisterFindUserInfoService<IndividualAccountUserLookup>()
    // ... other AuthP configuration methods left out

The table below provides states the basic format of the bulk loading with link to the service definitions which contains comments on the format. You can also see an example of these formats in the Example4AppAuthSetupData class.

What bulk load type service
Roles string containing lines IBulkLoadRolesService
Tenants string containing lines IBulkLoadTenantsService
Users List<DefineUserWithRolesTenant> IBulkLoadUsersService

Note that these bulk load methods only add Roles, Tenants and AuthUsers when there are no existing entries in the AuthP database.

User Admin

The authentication provider's users are the master list of users and the AuthP's AuthUsers need to be in synced to authentication provider's users. The RegisterAuthenticationProviderReader<TSync> extension method allows you to provide a service (that implements the ISyncAuthenticationUsers interface ) that AuthP can use to 'sync' its AuthUsers with the authentication provider's users.

The AuthP library contains a ISyncAuthenticationUsers for the Individual Account authentication provider, and its called SyncIndividualAccountUsers, but it is also shown below.

public class SyncIndividualAccountUsers : ISyncAuthenticationUsers
{
    private readonly UserManager<IdentityUser> _userManager;

    public SyncIndividualAccountUsers(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }

    /// <summary>
    /// This returns the userId, email and UserName of all the users
    /// </summary>
    /// <returns>collection of SyncAuthenticationUser</returns>
    public async Task<IEnumerable<SyncAuthenticationUser>> GetAllActiveUserInfoAsync()
    {
        return await _userManager.Users
            .Select(x => new SyncAuthenticationUser(x.Id, x.Email, x.UserName)).ToListAsync();
    }
}

If you are using a different authentication provider you need to create a service that implements the ISyncAuthenticationUsers and register the service using RegisterAuthenticationProviderReader<TSync> extension method.

See AuthUser's admin sync service for more details on how this works.

SuperUser

If you are using the Individual Accounts authentication provider, then when the Individual Accounts is created it will be empty of any users. That can be a problem when you have deployed the application to a new server, as no one is registered to access the admin features to add more users. The AddSuperUserToIndividualAccounts will create a new user in the Individual Accounts database on startup using information in your appsettings.json file shown below:

  "SuperAdmin":
  {
    "Email": "Super@g1.com",
    "Password": "Super@g1.com"
  }

This, combined with the Bulk Load extension methods you can create a AuthUser that has the AccessAll Permission member (seeSetting up your Permissions - access for more info). This then allows you to use this SuperUser to set up normal admin users.

NOTE: There is a small security issue in that if someone changes the SuperUser settings in the appsettings.json file, then you would get an extra SuperUser when the application is next deployed.

Finalize

The final extension method in the AuthP setup register all the services and, optionally, run some migration/seeding on startup of the ASP.NET Core. There are three options to chose from:

1. SetupAspNetCoreAndDatabase extension method

This method will apply a migration to the AuthP database on startup, and then seeds the AuthP database with any Bulk Load. To handle the situation where your application is running multiple instances it makes sure any migrations etc. are run inside a lock on a global resource, such as a database. That way it can ensure that the various accesses to the database aren't run at the same time.

The SetupAspNetCoreAndDatabase has a number of parts to it and it has its own SetupAspNetCoreAndDatabase page. Please go to that page for more details on how to use the SetupAspNetCoreAndDatabase extension method.

2. SetupAspNetCorePart extension method

This assumes that any databases has already been created/migrated, especially the AuthP's DbContext. Note: this approach doesn't support Bulk loading.

3. SetupForUnitTestingAsync extension method

This is used for unit testing - see the Unit Test your AuthP app page for more on how to unit test applications that use AuthP.

Articles / Videos

Concepts

Setup

Usage

Admin

SupportCode

Clone this wiki locally