How to Customize the SignIn Manager for ABP Applications

After creating a new application using the application startup template, you may want extend or change the default behavior of the SignIn Manager for your authentication and registration flow needs. ABP Account Module uses the Identity Management Module for SignIn Manager and the Identity Management Module uses default Microsoft Identity SignIn Manager (see here).

To write your Custom SignIn Manager, you need to extend Microsoft Identity SignIn Manager class and register it to the DI container.

This document explains how to customize the SignIn Manager for your own application.

Create a CustomSignInManager

Create a new class inheriting the SignInMager of Microsoft Identity package.

public class CustomSignInManager : Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>
{
        public CustomSignInManager(
            Microsoft.AspNetCore.Identity.UserManager<Volo.Abp.Identity.IdentityUser> userManager,
            Microsoft.AspNetCore.Http.IHttpContextAccessor contextAccessor,
            Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser> claimsFactory,
            Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor,
            Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>> logger,
            Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemes,
            Microsoft.AspNetCore.Identity.IUserConfirmation<Volo.Abp.Identity.IdentityUser> confirmation)
            : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
        {
        }
}

It is important to use Volo.Abp.Identity.IdentityUser type for SignInManager to inherit, not the AppUser of your application.

Afterwards you can override any of the SignIn Manager methods you need and add new methods and properties needed for your authentication or registration flow.

Overriding the GetExternalLoginInfoAsync Method

In this case we'll be overriding the GetExternalLoginInfoAsync method which is invoked when a third party authentication is implemented.

A good way to override a method is copying its source code. In this case, we will be using a minorly modified version of the source code which explicitly shows the namespaces of the methods and properties to help better understanding of the concept.

public override async Task<Microsoft.AspNetCore.Identity.ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
{
    var auth = await Context.AuthenticateAsync(Microsoft.AspNetCore.Identity.IdentityConstants.ExternalScheme);
    var items = auth?.Properties?.Items;
    if (auth?.Principal == null || items == null || !items.ContainsKey(LoginProviderKey))
    {
        return null;
    }

    if (expectedXsrf != null)
    {
        if (!items.ContainsKey(XsrfKey))
        {
            return null;
        }
        var userId = items[XsrfKey] as string;
        if (userId != expectedXsrf)
        {
            return null;
        }
    }

    var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
    var provider = items[LoginProviderKey] as string;
    if (providerKey == null || provider == null)
    {
        return null;
    }

    var providerDisplayName = (await GetExternalAuthenticationSchemesAsync()).FirstOrDefault(p => p.Name == provider)?.DisplayName
        ?? provider;
    return new Microsoft.AspNetCore.Identity.ExternalLoginInfo(auth.Principal, provider, providerKey, providerDisplayName)
    {
        AuthenticationTokens = auth.Properties.GetTokens()
    };
}

To get your overridden method invoked and your customized SignIn Manager class to work, you need to register your class to the Dependency Injection System.

Register to Dependency Injection

Registering CustomSignInManager should be done with adding AddSignInManager extension method of the IdentityBuilderExtensions of the IdentityBuilder.

Inside your .Web project, locate the YourProjectNameWebModule and add the following code under the PreConfigureServices method to replace the old SignInManager with your customized one:

PreConfigure<IdentityBuilder>(identityBuilder =>
{
    identityBuilder.AddSignInManager<CustomSignInManager>();
});

The Source Code

You can find the source code of the completed example here.

See Also

In this document