Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 445
Kok How Teh Avatar asked Jun 10 '19 05:06

Kok How Teh


People also ask

What is authentication scheme in ASP.NET Core?

Authentication is the process of determining a user's identity. Authorization is the process of determining whether a user has access to a resource. In ASP.NET Core, authentication is handled by the authentication service, IAuthenticationService, which is used by authentication middleware.

What does HttpContext SignInAsync do?

SignInAsync(HttpContext, String, ClaimsPrincipal, AuthenticationProperties) Sign in a principal for the specified scheme.

What is cookie based authentication in C#?

The entire cookie-based authentication works in the following manner: The user gives a username and password at the time of login. Once the user fills in the login form, the browser (client) sends a login request to the server. The server verifies the user by querying the user data.


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
like image 174
Erikest Avatar answered Oct 01 '22 16:10

Erikest