In a recent project, I had to add support for multiple identity providers. Some endpoints/controllers will be restricted to a particular identity provider while others can be authenticated with either identity provider.
Even though we only had one identity provider, we actually already constructed it in a way where it’s a collection of multiple providers. So the first requirement should already work.
// identityProvidersConfigs is a list of configurations for each identity provider
foreach (var idpConfig in identityProvidersConfigs)
{
var authScheme = idpConfig.Key; // Key here is the name of the authorization scheme
authBuilder.AddJwtBearer(authScheme, o =>
{
// Configuration for each authorization scheme here...
});
}
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(authSchemes.ToArray());
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
Since we already register all the authorization schemes, I thought it should be straightforward to restrict a controller to use one participant authorization scheme. Let’s say there are 2 authorizations schemes AuthSchemeFirst and AuthSchemeSecond
[ApiController]
[Authorize(AuthenticationSchemes = "AuthSchemeSecond")]
[Route("[controller]")]
public class SampleController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Content("Hello world!");
}
}
However, when I tested this controller, it worked with both the bearer tokens from AuthSchemeFirst and AuthSchemeSecond. It basically behaved like the plain [Authorize].
After a bit of research and trial and error, I figured that because the default policy was defined in the AddAuthorization
call, the default scheme was ignored and used the default policy instead. Since the default policy was for all the authorization schemes, hence tokens from both schemes were accepted.
To fix this, remove the default policy and add a policy for each authorization schema.
services.AddAuthorization(options =>
{
foreach (var authScheme in authSchemes)
{
options.AddPolicy(authScheme, new AuthorizationPolicyBuilder(authScheme)
.RequireAuthenticatedUser()
.Build());
}
});
The specified scheme should now work as expected.