title: AddOpenIdConnect() and DefaultPolicy as FallbackPolicy cause signin-oidc loop
body: <p>I had a <strong>.NET 5 Blazor Server</strong> application working with authentication against an <strong>Identity Server 4</strong> provider. The app is hosted in a Kubernetes cluster fronted by Nginx Ingress controller.</p>
  <p>The original app had the following code in <code>Startup.cs</code></p>
  <pre><code>...
  services
      .AddAuthentication(opt =&gt; {
          opt.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
          opt.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
          opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
      })
      .AddCookie()
      .AddOpenIdConnect(&quot;oidc&quot;, opt =&gt; {
          opt.Authority = Configuration[&quot;oidc:Authority&quot;];
          opt.ClientId = Configuration[&quot;oidc:ClientId&quot;];
          opt.ClientSecret = Configuration[&quot;oidc:ClientSecret&quot;];
          opt.Scope.Add(&quot;openid&quot;);
          opt.Scope.Add(&quot;profile&quot;);
  
          opt.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = &quot;name&quot; };
  
          opt.ResponseType = &quot;code id_token&quot;;       // id_token is needed for Hybrid Grant Type
  
          opt.SaveTokens = true;
          opt.GetClaimsFromUserInfoEndpoint = true;
  
          opt.Events = new OpenIdConnectEvents {
              OnAccessDenied = context =&gt; {
                  context.HandleResponse();
                  context.Response.Redirect(&quot;/&quot;);
                  return Task.CompletedTask;
              }
          };
      });
  ...
  // Required to use HTTPS for redirect_uri during authentication -- https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-5.0
  var forwardedHeaderOptions = new ForwardedHeadersOptions() {
      ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedProto
  };
  
  app.UseForwardedHeaders(forwardedHeaderOptions);
  
  app.UseAuthentication();
  app.UseAuthorization();
  
  app.UseEndpoints(endpoints =&gt; {
      endpoints.MapBlazorHub();
      endpoints.MapFallbackToPage(&quot;/_Host&quot;);
  });
  
  </code></pre>
  <p>The <code>LoginDisplay.razor</code> file had the &quot;usual&quot; (as seen in some &quot;older&quot; articles) set up</p>
  <pre><code>...
  &lt;NotAuthorized&gt;
     &lt;a class=&quot;ml-3&quot; href=&quot;login?redirectUri=/&quot;&gt;Log in&lt;/a&gt;
  &lt;/NotAuthorized&gt;
  </code></pre>
  <p>with the <code>Login.cshtml.cs</code> file containing</p>
  <pre><code>public class LoginModel :
      PageModel {
  
      public async Task OnGet(string redirectUri) {
          await HttpContext.ChallengeAsync(&quot;oidc&quot;, new AuthenticationProperties() { RedirectUri = redirectUri });
      }
  
  }
  </code></pre>
  <p>I decided to upgrade the application to .NET 6 (we are trying to resolve an unrelated issue with the k8s pod constantly restarting every few minutes for no apparent reason) while at the same time &quot;cleaning up&quot; the code. In my mind, it seems haphazard and dirty to need a &quot;login&quot; razor component that just calls the &quot;login.cshtml&quot; file.</p>
  <p>Another reason for the change is that we want all the views to automatically redirect the user to authentication. In other words, no need for the user to click the &quot;Log in&quot; link.</p>
  <p>The new code is very, very similar in that the <code>Program.cs</code> file has the following (in .NET 6, the Startup.cs and Program.cs have been merged)</p>
  <pre><code>builder.Services
      .AddAuthentication(options =&gt; {
          options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
          
          // Have tried setting other Scheme properties
          //options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
          //options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  
          // Have tried **both** OpenIdConnectDefaults.AuthenticationScheme and &quot;oidc&quot;
          options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
          //options.DefaultChallengeScheme = &quot;oidc&quot;;
      })
      .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
      //.AddOpenIdConnect(&quot;oidc&quot;, options =&gt; {
      .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =&gt; {
          // Have try both with and without setting these options
          //options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
          //options.SignOutScheme = OpenIdConnectDefaults.AuthenticationScheme;
  
          options.Authority = ssoSettings.Authority;
  
          options.ClientId = ssoSettings.ClientId;
          options.ClientSecret = ssoSettings.ClientSecret;
  
          options.Scope.Add(&quot;openid&quot;);
          options.Scope.Add(&quot;profile&quot;);
  
          options.ResponseType = &quot;code id_token&quot;;
          options.GetClaimsFromUserInfoEndpoint = true;
          options.SaveTokens = true;
  
          options.TokenValidationParameters = new() {
              NameClaimType = &quot;name&quot;
          };
  
          options.Events = new OpenIdConnectEvents {
              OnAccessDenied = context =&gt; {
                  context.HandleResponse();
                  context.Response.Redirect(&quot;/&quot;);
                  return Task.CompletedTask;
              }
          };
      });
  </code></pre>
  <p>The big difference is our use of <code>FallbackPolicy</code> to enforce authorization on all calls</p>
  <pre><code>builder.Services.AddAuthorization(options =&gt; {
      // By default, all incoming requests will be authorized according to the default policy
      options.FallbackPolicy = options.DefaultPolicy;
  });
  </code></pre>
  <p>The result ends up being that whenever the Identity Server redirects back to <code>/signin-oidc</code>, that endpoint returns another <code>302</code> sending the user back to the identity server and therefore causing a loop.</p>
  <p>However, this <strong>only</strong> happens when the application is deployed to our Kubernetes cluster. I  <strong>cannot</strong> get this to happen locally when debugging in Visual Studio. In VS, everything works as expected.</p>
  <p>I also noticed that in the K8s logs, the following entries</p>
  <pre><code>{&quot;EventId&quot;:1,&quot;LogLevel&quot;:&quot;Warning&quot;,&quot;Category&quot;:&quot;Microsoft.AspNetCore.Http.ResponseCookies&quot;,&quot;Message&quot;:&quot;The cookie \u0027.AspNetCore.OpenIdConnect.Nonce.CfDJ8HJzxP7cycdIvDHSkH_UahcueTVoI4VJZuIcSNHCrfknCfeAbVENZr7-FTCN9iXO1k1D4Q3QPjkzuwFIrfdAhHBkQe48lPbdssB4uN7_9_ueE5AhkSlG6-MUqIaXTtfar4ZZD7h65wjyAKuTuHhkfdNIAhDB170xb-gAd8CzOApVuaY9JMhcU_w-kt_kvIx04mQPmPxNr-uU9NUEhxyME1ZPrRkqSg2gLgnlyJEfTheyU85o2R4z-IhqhYcFb80wvIu6okozDqqpA29kLQ434RU\u0027 has set \u0027SameSite=None\u0027 and must also set \u0027Secure\u0027.&quot;,&quot;State&quot;:{&quot;Message&quot;:&quot;The cookie \u0027.AspNetCore.OpenIdConnect.Nonce.CfDJ8HJzxP7cycdIvDHSkH_UahcueTVoI4VJZuIcSNHCrfknCfeAbVENZr7-FTCN9iXO1k1D4Q3QPjkzuwFIrfdAhHBkQe48lPbdssB4uN7_9_ueE5AhkSlG6-MUqIaXTtfar4ZZD7h65wjyAKuTuHhkfdNIAhDB170xb-gAd8CzOApVuaY9JMhcU_w-kt_kvIx04mQPmPxNr-uU9NUEhxyME1ZPrRkqSg2gLgnlyJEfTheyU85o2R4z-IhqhYcFb80wvIu6okozDqqpA29kLQ434RU\u0027 has set \u0027SameSite=None\u0027 and must also set \u0027Secure\u0027.&quot;,&quot;name&quot;:&quot;.AspNetCore.OpenIdConnect.Nonce.CfDJ8HJzxP7cycdIvDHSkH_UahcueTVoI4VJZuIcSNHCrfknCfeAbVENZr7-FTCN9iXO1k1D4Q3QPjkzuwFIrfdAhHBkQe48lPbdssB4uN7_9_ueE5AhkSlG6-MUqIaXTtfar4ZZD7h65wjyAKuTuHhkfdNIAhDB170xb-gAd8CzOApVuaY9JMhcU_w-kt_kvIx04mQPmPxNr-uU9NUEhxyME1ZPrRkqSg2gLgnlyJEfTheyU85o2R4z-IhqhYcFb80wvIu6okozDqqpA29kLQ434RU&quot;,&quot;{OriginalFormat}&quot;:&quot;The cookie \u0027{name}\u0027 has set \u0027SameSite=None\u0027 and must also set \u0027Secure\u0027.&quot;}}
  {&quot;EventId&quot;:1,&quot;LogLevel&quot;:&quot;Warning&quot;,&quot;Category&quot;:&quot;Microsoft.AspNetCore.Http.ResponseCookies&quot;,&quot;Message&quot;:&quot;The cookie \u0027.AspNetCore.Correlation.pvnQ3UKTs2JF6O6CTBUG3cUuoLtj7_Gm16zOzgsWFVM\u0027 has set \u0027SameSite=None\u0027 and must also set \u0027Secure\u0027.&quot;,&quot;State&quot;:{&quot;Message&quot;:&quot;The cookie \u0027.AspNetCore.Correlation.pvnQ3UKTs2JF6O6CTBUG3cUuoLtj7_Gm16zOzgsWFVM\u0027 has set \u0027SameSite=None\u0027 and must also set \u0027Secure\u0027.&quot;,&quot;name&quot;:&quot;.AspNetCore.Correlation.pvnQ3UKTs2JF6O6CTBUG3cUuoLtj7_Gm16zOzgsWFVM&quot;,&quot;{OriginalFormat}&quot;:&quot;The cookie \u0027{name}\u0027 has set \u0027SameSite=None\u0027 and must also set \u0027Secure\u0027.&quot;}}
  </code></pre>
  <p>While I understand I should update my Identity Server to set <code>secure</code>, this same server was <strong>already</strong> working with the previous version of the application. And it still works (and I do not see that warning anywhere) when I run the app locally, so I am unsure if this warning is related to the redirect loop.</p>
  <p>One of my issues is finding <strong>recent</strong> articles/examples on Blazor <strong>Server</strong> and authentication that does not involve Azure AD or ASP.NET Identity (local accounts). Too many links are about Blazor WASM. Even the Identity Server 4 website, with its examples, is still limited to MVC. Are there any <strong>current</strong> examples out there that somebody has run across?</p>