title: ASP.NET Core Identity Exception for "Identity.External"
body: <p>I'm having a weird problem with an ASP.NET Core 2.1 site. When I sign into it, and refresh it 30 minutes later, I always get this exception thrown:</p>
  
  <blockquote>
    <p>InvalidOperationException: No sign-out authentication handler is registered for the scheme 'Identity.External'. The registered sign-out schemes are: Identity.Application. Did you forget to call AddAuthentication().AddCookies("Identity.External",...)?</p>
  </blockquote>
  
  <p>It's correct that I don't have <code>Identity.External</code> registered, but I also don't want it registered. Why does it keep trying to sign it out? Here's how I'm registering my cookie:</p>
  
  <pre><code>services.AddAuthentication(
      o =&gt; {
          o.DefaultScheme = IdentityConstants.ApplicationScheme;
      }).AddCookie(IdentityConstants.ApplicationScheme,
      o =&gt; {
          o.Events = new CookieAuthenticationEvents {
              OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
          };
      });
  
  services.ConfigureApplicationCookie(
      o =&gt; {
          o.Cookie.Expiration = TimeSpan.FromHours(2);
          o.Cookie.HttpOnly = true;
          o.Cookie.SameSite = SameSiteMode.Strict;
          o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  
          o.AccessDeniedPath = "/admin";
          o.LoginPath = "/admin";
          o.LogoutPath = "/admin/sign-out";
          o.SlidingExpiration = true;
      });
  </code></pre>
  
  <p>Could someone point me in the right direction on how to resolve this?</p>
  
  <p><strong>UPDATE</strong></p>
  
  <p>Here's the complete code and use process as requested by @Edward in the comments. I'm omitting some parts for brevity.</p>
  
  <p><code>Startup.cs</code></p>
  
  <pre><code>public sealed class Startup {
      public void ConfigureServices(
          IServiceCollection services) {
          //  ...
          services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
          services.AddApplicationIdentity();
          services.AddScoped&lt;ApplicationSignInManager&gt;();
  
          services.Configure&lt;IdentityOptions&gt;(
              o =&gt; {
                  o.Password.RequiredLength = 8;
  
                  o.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                  o.Lockout.MaxFailedAccessAttempts = 5;
              });
          services.ConfigureApplicationCookie(
              o =&gt; {
                  o.Cookie.Name = IdentityConstants.ApplicationScheme;
                  o.Cookie.Expiration = TimeSpan.FromHours(2);
                  o.Cookie.HttpOnly = true;
                  o.Cookie.SameSite = SameSiteMode.Strict;
                  o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
  
                  o.AccessDeniedPath = "/admin";
                  o.LoginPath = "/admin";
                  o.LogoutPath = "/admin/sign-out";
                  o.SlidingExpiration = true;
              });
          //  ...
      }
  
      public void Configure(
          IApplicationBuilder app) {
          //  ...
          app.UseAuthentication();
          //  ...
      }
  }
  </code></pre>
  
  <p><code>ServiceCollectionExtensions.cs</code></p>
  
  <pre><code>public static class ServiceCollectionExtensions {
      public static IdentityBuilder AddApplicationIdentity(
          this IServiceCollection services) {
          services.AddAuthentication(
              o =&gt; {
                  o.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
                  o.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
                  o.DefaultForbidScheme = IdentityConstants.ApplicationScheme;
                  o.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
                  o.DefaultSignOutScheme = IdentityConstants.ApplicationScheme;
              }).AddCookie(IdentityConstants.ApplicationScheme,
              o =&gt; {
                  o.Events = new CookieAuthenticationEvents {
                      OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
                  };
              });
  
          services.TryAddScoped&lt;SignInManager&lt;User&gt;, ApplicationSignInManager&gt;();
          services.TryAddScoped&lt;IPasswordHasher&lt;User&gt;, PasswordHasher&lt;User&gt;&gt;();
          services.TryAddScoped&lt;ILookupNormalizer, UpperInvariantLookupNormalizer&gt;();
          services.TryAddScoped&lt;IdentityErrorDescriber&gt;();
          services.TryAddScoped&lt;ISecurityStampValidator, SecurityStampValidator&lt;User&gt;&gt;();
          services.TryAddScoped&lt;IUserClaimsPrincipalFactory&lt;User&gt;, UserClaimsPrincipalFactory&lt;User&gt;&gt;();
          services.TryAddScoped&lt;UserManager&lt;User&gt;&gt;();
          services.TryAddScoped&lt;IUserStore&lt;User&gt;, ApplicationUserStore&gt;();
  
          return new IdentityBuilder(typeof(User), services);
      }
  }
  </code></pre>
  
  <p><code>DefaultController.cs</code></p>
  
  <pre><code>[Area("Admin")]
  public sealed class DefaultController :
      AdminControllerBase {
      [HttpPost, AllowAnonymous]
      public async Task&lt;IActionResult&gt; SignIn(
          SignIn.Command command) {
          var result = await Mediator.Send(command);
  
          if (result.Succeeded) {
              return RedirectToAction("Dashboard", new {
                  area = "Admin"
              });
          }
  
          return RedirectToAction("SignIn", new {
              area = "Admin"
          });
      }
  
      [HttpGet, ActionName("sign-out")]
      public async Task&lt;IActionResult&gt; SignOut() {
          await Mediator.Send(new SignOut.Command());
  
          return RedirectToAction("SignIn", new {
              area = "Admin"
          });
      }
  }
  </code></pre>
  
  <p><code>SignIn.cs</code></p>
  
  <pre><code>public sealed class SignIn {
      public sealed class Command :
          IRequest&lt;SignInResult&gt; {
          public string Password { get; set; }
          public string Username { get; set; }
      }
  
      public sealed class CommandHandler :
          HandlerBase&lt;Command, SignInResult&gt; {
          private ApplicationSignInManager SignInManager { get; }
  
          public CommandHandler(
              DbContext context,
              ApplicationSignInManager signInManager)
              : base(context) {
              SignInManager = signInManager;
          }
  
          protected override SignInResult Handle(
              Command command) {
              var result = SignInManager.PasswordSignInAsync(command.Username, command.Password, true, false).Result;
  
              return result;
          }
      }
  }
  </code></pre>
  
  <p><code>SignOut.cs</code></p>
  
  <pre><code>public sealed class SignOut {
      public sealed class Command :
          IRequest {
      }
  
      public sealed class CommandHandler :
          HandlerBase&lt;Command&gt; {
          private ApplicationSignInManager SignInManager { get; }
  
          public CommandHandler(
              DbContext context,
              ApplicationSignInManager signInManager)
              : base(context) {
              SignInManager = signInManager;
          }
  
          protected override async void Handle(
              Command command) {
              await SignInManager.SignOutAsync();
          }
      }
  }
  </code></pre>
  
  <p>There's all the relevant code, from how I configure the identity to how I sign in and out. I'm still at a loss of why <code>Identity.External</code> is coming into the picture when I never asked for it.</p>
  
  <p>Technically the <code>SignIn</code> and <code>SignOut</code> classes can be removed and their functionality merged into the <code>DefaultController</code>, however I opt into keeping them to keep the application structure consistent.</p>