title: ASP.NET Core 6 : add multiple authentication schemes with multiple authorization policies along with dependency injection
body: <p>I need to create a framework for coworkers that allows for multiple authentication schemes and correlating authorization policies (since our IDP has multiple allowed approaches), but those schemes require dependency injection because the IDP information is provided from a cloud-based configuration source. The authentication is done with a JWT Bearer token in the cases that I'm working with so far.</p>
  <p>I want to stick to the most current practices to be as current as possible. So it appears that I should be using <code>AddAuthentication</code>, <code>AddJwtBearer</code>, and <code>AddAuthorization</code> on my <code>IServiceCollection</code>.</p>
  <p>I am expecting a controller or endpoint can be decorated with the <code>AuthorizeAttribute</code> and it will employ the designated default policy, which will use the designated default authN scheme. If the attribute is given constructor parameters of <code>Policy = &lt;Some Non Default Policy&gt;</code> or <code>AuthenticationSchemes = &lt;Some other scheme&gt;</code>, it will switch to those instead.</p>
  <p>First, I established that I needed to use dependency injection, so I'm using <code>IConfigureNamedOptions&lt;JwtBearerOptions&gt;</code>.</p>
  <p>So I created the options class like this for auth code</p>
  <pre><code>public class AuthCodeJwtBearerOptions : IConfigureNamedOptions&lt;JwtBearerOptions&gt;
  {
      private readonly IClaimsTransformation _claimsTransformation;
      private readonly ConfigurationManager&lt;OpenIdConnectConfiguration&gt; _configurationManager;
  
      public AuthCodeJwtBearerOptions(IOptions&lt;TidV4OAuthSettings&gt; tidV4OAuthOptions,
          IClaimsTransformation claimsTransformation)
      {
          _claimsTransformation = claimsTransformation;
  
          _configurationManager =
              new ConfigurationManager&lt;OpenIdConnectConfiguration&gt;(tidV4OAuthOptions.Value.WellknownUrl,
                  new OpenIdConnectConfigurationRetriever());
      }
  
      public void Configure(JwtBearerOptions options) =&gt; Configure(&quot;AuthCode&quot;, options);
  
      public void Configure(string name, JwtBearerOptions options)
      {
          var task = Task.Run(async () =&gt; await GetTokenValidationParametersAsync());
          options.TokenValidationParameters = task.Result;
          options.Events = new JwtBearerEvents { OnTokenValidated = OnTokenValidated };
      }
  
      private async Task&lt;TokenValidationParameters&gt; GetTokenValidationParametersAsync()
      {
          var cancellationToken = new CancellationToken();
          var openIdConnectConfiguration = await _configurationManager.GetConfigurationAsync(cancellationToken);
          return new TokenValidationParameters
          {
              ValidateIssuerSigningKey = true,
              IssuerSigningKeys = openIdConnectConfiguration?.SigningKeys,
              ValidateAudience = false,
              ValidateIssuer = true,
              ValidIssuer = openIdConnectConfiguration?.Issuer,
              ValidateLifetime = true
          };
      }
  
      private async Task OnTokenValidated(TokenValidatedContext context)
      {
          if (context.Principal == null)
          {
              return;
          }
  
          context.Principal = await _claimsTransformation.TransformAsync(context.Principal);
      }
  }
  </code></pre>
  <p>And I then repeated this pattern for a class called <code>ClientCredentialsJwtBearerOptions</code>, but I have this variation</p>
  <pre><code>public void Configure(JwtBearerOptions options) =&gt; Configure(&quot;ClientCredentials&quot;, options);
  </code></pre>
  <p>I register all of this by setting the default authentication scheme, Adding the JWT Bearers by name with an empty delegate, and then I call to configure the options.</p>
  <p>Then I assign policies. I may be wrong, but I don't think the policy creation is the issue, but I'll include the code in case it is a problem.</p>
  <pre><code>serviceCollection
              .AddAuthentication(options =&gt;
              {
                  options.DefaultAuthenticateScheme = &quot;AuthCode&quot;;
                  options.DefaultChallengeScheme = &quot;AuthCode&quot;;
              })
              .AddJwtBearer(&quot;AuthCode&quot;, _ =&gt; { })
              .AddJwtBearer(&quot;ClientCredentials&quot;, _ =&gt; { });
  
          serviceCollection.ConfigureOptions&lt;ClientCredentialsJwtBearerOptions&gt;();
          serviceCollection.ConfigureOptions&lt;AuthCodeJwtBearerOptions&gt;();
  
          serviceCollection.AddAuthorization(options =&gt;
          {
              var authCodePolicy = new AuthorizationPolicyBuilder()
                  .RequireAuthenticatedUser()
                  .AddAuthenticationSchemes(&quot;AuthCode&quot;)
                  .Build();
  
              var clientCredentialsPolicy = new AuthorizationPolicyBuilder()
                  .RequireAuthenticatedUser()
                  .AddAuthenticationSchemes(&quot;ClientCredentials&quot;)
                  .Build();
  
              var allPolicy = new AuthorizationPolicyBuilder()
                  .RequireAuthenticatedUser()
                  .AddAuthenticationSchemes(&quot;AuthCode&quot;, &quot;ClientCredentials&quot;)
                  .Build();
  
              options.AddPolicy(&quot;AuthCodeOnly&quot;, authCodePolicy);
              options.AddPolicy(&quot;ClientCredentialsOnly&quot;, clientCredentialsPolicy);
              options.AddPolicy( &quot;AllPolicies&quot;, allPolicy);
  
              options.DefaultPolicy = authCodePolicy;
          });
  </code></pre>
  <p>I am using constants for the strings here, but I wrote literal strings for ease of reading the example.</p>
  <p>When I test this, everything works fine if I leave it exactly like this. And it uses the <code>AuthCodeJwtBearerOptions</code> and the request goes through.</p>
  <p>But if I change the <code>Authorize</code> attribute to</p>
  <pre><code>[Authorize(Policy = &quot;ClientCredentialsOnly&quot;)]
  </code></pre>
  <p>Authentication still uses <code>AuthCodeJwtBearerOptions</code>. I can get it to switch by simply reversing the order of the <code>Configure</code> call</p>
  <pre><code>serviceCollection.ConfigureOptions&lt;AuthCodeJwtBearerOptions&gt;();
  serviceCollection.ConfigureOptions&lt;ClientCredentialsJwtBearerOptions&gt;();        
  </code></pre>
  <p>Suggesting to me that it's just using the last one to be registered and it is not respecting the &quot;Named&quot; functionality of named configurations.</p>
  <p>And if I change the default policy, nothing changes.</p>
  <p>I feel like I've got most of the pieces that I need to get this working and I'm just misunderstanding what <code>ConfigureOptions</code> does.</p>
  <p>Any help is appreciated. Thank you.</p>