[Bug] RequiredScope doesn't work when securing controllers by setting AuthorizationOptions.FallbackPolicy
Which version of Microsoft Identity Web are you using? Microsoft Identity Web 1.22.1
Where is the issue?
- Web app
- [ ] Sign-in users
- [ ] Sign-in users and call web APIs
- Web API
- [ ] Protected web APIs (validating tokens)
- [x] Protected web APIs (validating scopes)
- [ ] Protected web APIs call downstream web APIs
- Token cache serialization
- [ ] In-memory caches
- [ ] Session caches
- [ ] Distributed caches
- Other (please describe)
Is this a new or an existing app? This is a new app or an experiment.
Repro
I'm trying to protect an API controller method (here in the standard WeatherForecastController sample controller) with the RequiredScopeAttribute, so that only calls having the Weather.Read scope succeed.
[RequiredScope("Weather.Read")]
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
I have configured JWT Bearer authentication as follows in Program.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Note that with the statement
builder.Services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);
Authentication should be required for all controllers.
I have also configured an AAD application for the API, exposing two scopes, Weather.Read and Data.Read.
Expected behavior
When calling the endpoint with scope Data.Read but without Weather.Read, the call should be unauthorized.
Actual behavior
When calling the endpoint with scope Data.Read but without Weather.Read, the call succeeds. The security check used to work however with previous versions of the library. I've done the test with the following versions:
- 1.16.1 => OK (unauthorized)
- 1.17.0 => OK (unauthorized)
- 1.18.0 => scope not checked (200 response)
- 1.19.0 => scope not checked (200 response)
- 1.20.0 => scope not checked (200 response)
- 1.22.1 => scope not checked (200 response)
Note that if I require authorization by other means than setting the FallbackPolicy, the scope check works, even in 1.22.1, for example:
- Specifying
app.MapControllers().RequireAuthorization();inProgram.cs. - Adding an
[Authorize]attribute for the whole controller.
If I add a manual check in the controller method, the check also detects that the required scope Weather.Read is missing:
HttpContext.VerifyUserHasAnyAcceptedScope("Weather.Read");
Possible solution
Additional context / logs / screenshots / link to code
@DavidFowl Can you assist with this? This is related to the changes we did for moving from Filters to Policies. Appreciate any advice on how to resolve this.
This is still not solved in Microsoft.Identitty.Web 2.12.4. This is a security problem and should be fixed urgently ! @davidfowl, @jennyf19, any input ?