title: Middleware Ordering
body: <p>I have a new .NET Core 3.1 app and am struggling with the concept of Middleware. From reading around, it seems the order of including different middlewares is important. I currently have several problems which I can't seem to solve:</p>
  <ol>
  <li>I never see the developer error page, and have to check the event log to see what's happened if there's an error. I just get the blank &quot;error 500&quot; etc pages from Chrome. The custom error pages also never display when there's a 500/400.</li>
  <li>The app always tries to redirect me to <em>/Account/Login</em> despite changing this in the cookie settings.</li>
  <li><em>User.IsAuthenticated</em> returns false when the <em>CheckPermissionsAction</em> call is made in Elmah, so I can't access Elmah. The <em>User.IsInRole</em> call works for from controllers though.</li>
  </ol>
  <p>This is how I'm bootstrapping the app. It feels like something is overriding the settings:</p>
  <pre><code>public void ConfigureServices(IServiceCollection services)
      {
          services.Configure&lt;DataProtectionTokenProviderOptions&gt;(options =&gt;
              options.TokenLifespan = TimeSpan.FromDays(2));
  
          services.AddSession(options =&gt;
          {
              options.IdleTimeout = TimeSpan.FromMinutes(30);
          });
          services.AddControllersWithViews();
          services.AddTransient&lt;IUserStore&lt;User&gt;, UserStore&gt;();
          services.AddTransient&lt;IRoleStore&lt;IdentityRole&gt;, RoleStore&gt;();
          services.AddRazorPages();
          
  
          services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
              .AddCookie(options =&gt;
              {
                  options.LoginPath = new PathString(&quot;/login&quot;);
                  options.AccessDeniedPath = new PathString(&quot;/error/denied&quot;);
                  options.LogoutPath = new PathString(&quot;/log-off&quot;);
                  options.ExpireTimeSpan = TimeSpan.FromDays(60);
                  options.SlidingExpiration = true;
                  options.Cookie.HttpOnly = true;
                  options.Cookie.Name = &quot;MyCookie&quot;;
                  options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
                  options.Cookie.SameSite = SameSiteMode.Lax;
              });
          services.AddIdentity&lt;User, IdentityRole&gt;(options =&gt;
          {
              options.Password.RequireDigit = true;
              options.Password.RequiredLength = 6;
              options.Password.RequireNonAlphanumeric = false;
              options.Password.RequireUppercase = false;
              options.Password.RequireLowercase = false;
          })
          .AddUserStore&lt;UserStore&gt;()
          .AddRoleStore&lt;RoleStore&gt;()
          .AddDefaultTokenProviders();
          services.Configure&lt;CookiePolicyOptions&gt;(options =&gt;
          {
              // This lambda determines whether user consent for non-essential cookies is needed for a given request.
              options.CheckConsentNeeded = context =&gt; false;
              options.MinimumSameSitePolicy = SameSiteMode.None;
          });
  
          services.AddElmah&lt;SqlErrorLog&gt;(options =&gt;
          {
              options.ConnectionString = Configuration.GetConnectionString(&quot;MyApp&quot;);
              options.CheckPermissionAction = (context)=&gt;{
                  return context.User.Identity.IsAuthenticated &amp;&amp; context.User.IsInRole(RoleHelper.SuperAdmin);
              };
              options.Path = &quot;/elmah&quot;;
          });
          services.AddSingleton&lt;IAppConfiguration, AppConfiguration&gt;(e =&gt; Configuration.GetSection(&quot;AppConfig&quot;)
             .Get&lt;AppConfiguration&gt;());
  
          OptionsConfigurationServiceCollectionExtensions.Configure&lt;DbHelper&gt;(services, Configuration.GetSection(&quot;ConnectionStrings&quot;));
          services.AddHttpContextAccessor();
      }
  
      public void ConfigureContainer(ContainerBuilder builder)
      {
          // wire up using autofac specific APIs here
          builder.Register(context =&gt; new MapperConfiguration(cfg =&gt;
          {
              cfg.CreateMap&lt;User, MyDetailsViewModel&gt;();
          })).AsSelf().SingleInstance();
          builder.RegisterModule(new RegistrationModule()); // separate assembly, wires up autofac registrations
          builder.Register(c =&gt;
          {
              //This resolves a new context that can be used later.
              var context = c.Resolve&lt;IComponentContext&gt;();
              var config = context.Resolve&lt;MapperConfiguration&gt;();
              return config.CreateMapper(context.Resolve);
          })
          .As&lt;IMapper&gt;()
          .InstancePerLifetimeScope();
      }
  
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              // debugger shows this section is called, but I never see the error page.
              app.UseDeveloperExceptionPage();
              app.UseDatabaseErrorPage();
              app.UseRouteDebugger();
          }
          else
          {
              app.UseExceptionHandler(&quot;/error&quot;);
              // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
              app.UseHsts();
          }
  
          app.UseSession();
          app.UseElmah();
          app.UseHttpsRedirection();
          app.UseStaticFiles();
          app.UseRouting();
          var cookiePolicyOptions = new CookiePolicyOptions
          {
              Secure = CookieSecurePolicy.SameAsRequest,
              MinimumSameSitePolicy = SameSiteMode.None
          };
          app.UseCookiePolicy(cookiePolicyOptions);
          app.UseAuthentication();
          app.UseAuthorization();
  
          app.UseEndpoints(endpoints =&gt;
          {
              endpoints.MapDefaultControllerRoute();
              endpoints.MapControllerRoute(
                  name: &quot;default&quot;,
                  pattern: &quot;{controller=Guest}/{action=Index}/{id?}&quot;);
              endpoints.MapRazorPages();
              endpoints.MapControllers();
          });
  
          app.UseStatusCodePages(async ctx =&gt;
          {
              //Re-execute the request so the user gets the error page
              string originalPath = ctx.HttpContext.Request.Path.Value;
              switch (ctx.HttpContext.Response.StatusCode)
              {
                  case 401:
                      //Re-execute the request so the user gets the error page
                      ctx.HttpContext.Items[&quot;originalPath&quot;] = originalPath;
                      ctx.HttpContext.Request.Path = &quot;/error/denied&quot;;
                      break;
                  case 412:
                      ctx.HttpContext.Items[&quot;originalPath&quot;] = originalPath;
                      ctx.HttpContext.Request.Path = &quot;/error/expired-account&quot;;
                      break;
                  case 404:
                      ctx.HttpContext.Items[&quot;originalPath&quot;] = originalPath;
                      ctx.HttpContext.Request.Path = &quot;/error/not-found&quot;;
                      break;
                  case 500:
                      ctx.HttpContext.Items[&quot;originalPath&quot;] = originalPath;
                      ctx.HttpContext.Request.Path = &quot;/error/not-found&quot;;
                      break;
              }
          });
          DapperExtensions.DapperExtensions.SetMappingAssemblies(new[]
              {
                   Assembly.GetAssembly(typeof(MyApp.Domain.Model.Note)),
                   Assembly.GetExecutingAssembly()
          });
      }
  </code></pre>