ASP.Net Core 2.2 SignInManager 'No sign-in authentication handler is registered for the scheme 'Identity.Application'

Viewed 4895

I have the following configurations in Startup.cs:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    //options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o => {
    o.LoginPath = Routes.Urls.AdminAccountLogin;
    o.AccessDeniedPath = Routes.Urls.AdminAccountAccessdenied;
}).AddJwtBearer(configureOptions => {});

The application throws the following exception when the controller Login action calls SignInManger.PasswordSignInAsync:

Exception has occurred: CLR/System.InvalidOperationException
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll: 'No sign-in authentication handler is registered for the scheme 'Identity.Application'. The registered sign-in schemes are: Cookies. Did you forget to call AddAuthentication().AddCookies("Identity.Application",...)?'

Where does the Identity.Application come from?

1 Answers

Short (and not as helpful) Answer:

Specifically it comes from the microsoft.aspnetcore.identity package in the class Microsoft.AspNetCore.Identity.IdentityConstants.ApplicationScheme

Long Answer, with the whole breakdown:

You need to add Identity - That scheme is stood up and connected to authentication in the AddIdentity extension method

The extension method is in Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions

public static IdentityBuilder AddIdentity<TUser, TRole>(this IServiceCollection services, Action<IdentityOptions> setupAction) where TUser: class where TRole: class
        {
            services.AddAuthentication(delegate (AuthenticationOptions options) {
                options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
                options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
                options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            }).AddCookie(IdentityConstants.ApplicationScheme, delegate (CookieAuthenticationOptions o) {
                o.LoginPath = new PathString("/Account/Login");
                CookieAuthenticationEvents events1 = new CookieAuthenticationEvents();
                events1.OnValidatePrincipal = new Func<CookieValidatePrincipalContext, Task>(SecurityStampValidator.ValidatePrincipalAsync);
                o.Events = events1;
            }).AddCookie(IdentityConstants.ExternalScheme, delegate (CookieAuthenticationOptions o) {
                o.Cookie.Name = IdentityConstants.ExternalScheme;
                o.ExpireTimeSpan = TimeSpan.FromMinutes((double) 5.0);
            }).AddCookie(IdentityConstants.TwoFactorRememberMeScheme, delegate (CookieAuthenticationOptions o) {
                o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme;
                CookieAuthenticationEvents events1 = new CookieAuthenticationEvents();
                events1.OnValidatePrincipal = new Func<CookieValidatePrincipalContext, Task>(SecurityStampValidator.ValidateAsync<ITwoFactorSecurityStampValidator>);
                o.Events = events1;
            }).AddCookie(IdentityConstants.TwoFactorUserIdScheme, delegate (CookieAuthenticationOptions o) {
                o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme;
                o.ExpireTimeSpan = TimeSpan.FromMinutes((double) 5.0);
            });
            services.AddHttpContextAccessor();
            services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
            services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
            services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
            services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
            services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
            services.TryAddScoped<IdentityErrorDescriber>();
            services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
            services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
            services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
            services.TryAddScoped<UserManager<TUser>>();
            services.TryAddScoped<SignInManager<TUser>>();
            services.TryAddScoped<RoleManager<TRole>>();
            if (setupAction != null)
            {
                services.Configure<IdentityOptions>(setupAction);
            }
            return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
        }

If you follow this AddCookie call

.AddCookie(IdentityConstants.ApplicationScheme, delegate (CookieAuthenticationOptions o) {
                o.LoginPath = new PathString("/Account/Login");
                CookieAuthenticationEvents events1 = new CookieAuthenticationEvents();
                events1.OnValidatePrincipal = new Func<CookieValidatePrincipalContext, Task>(SecurityStampValidator.ValidatePrincipalAsync);
                o.Events = events1;

it eventually configures AuthenticationOptions with the "Identity.Application" scheme and a CookieAuthenticationHandler

When you call SignInManager.PasswordSignInAsync:

  • SignInManager checks the username/password in the database (and does two factor flow if enabled), then if good
  • creates the ClaimsPrincipal and sends it to HttpContext.SignInAsync (an extension method) with the identity application scheme, see here
  • Which gets the IAuthenticationService (added to DI by AddAuthentication), see here
  • In AuthenticationService, it uses a chain of objects
    • IAuthenticationHandlerProvider => IAuthenticationSchemeProvider => the previously configured AuthenticationOptions to construct an AuthenticationScheme which serves up the IAuthenticationHandler in this case CookieAuthenticationHandler. see here and here and here
    • CookieAuthenticationHandler.HandleSignInAsync creates, encrypts and adds the cookie.

Now the cookie is there, so the next request (often a redirect after login) in the AuthenticationMiddleware, the HttpContext.AuthenticateAsync method is called, which follows a similar flow to

  • CookieAuthenticationHandler.HandleAuthenticateAsync which reads the cookie and passes back a ClaimsPrincipal,
  • this is assigned to HttpContext.User, making it accessible to all the other areas of the request pipeline, like authorization, see here
Related