Problem Statement
A locally deployed dotnet core MVC app is returning a 403 Forbidden when I send a GET request to the health endpoint indicating the user is authenticated but not authorized to use that resource.
Questions
- Do I need to setup a User and Roles in Auth0 for this to work?
- Is there additional code needed for dotnet core Policy-Based Authorization than the code I've provided below?
- Since the ScopeHandler handles a Requirement asynchronously does my controller action need to be async?
Resources used so far
Auth0 tutorial for dotnet core Authorization
Relevant code
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
string domain = $"https://{Configuration["Auth0:domain"]}/";
services.AddAuthentication(opts => {
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opts => {
opts.Authority = domain;
opts.Audience = Configuration["Auth0:Identifier"];
opts.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("check:health", policy => policy.Requirements.Add(new HasScopeRequirement("check:health", domain)));
});
services.AddSingleton<IAuthorizationHandler, ScopeHandler>();
services.AddDbContext<PathosContext>(
options => options.UseSqlite(Configuration["PathosConnectionString"])
);
}
HasScopeRequirement.cs
public class HasScopeRequirement : IAuthorizationRequirement
{
public string Issuer { get; }
public string Scope { get; }
public HasScopeRequirement(string scope, string issuer)
{
Scope = scope ?? throw new ArgumentNullException(nameof(scope));
Issuer = issuer ?? throw new ArgumentNullException(nameof(issuer));
}
}
ScopeHandler.cs
public class ScopeHandler : AuthorizationHandler<HasScopeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == "scope" && c.Issuer == requirement.Issuer))
return Task.CompletedTask;
var scopes = context.User.FindFirst(c => c.Type == "scope" && c.Issuer == requirement.Issuer).Value.Split(' ');
if (scopes.Any(s => s == requirement.Scope))
context.Succeed(requirement);
return Task.CompletedTask;
}
}
HealthController.cs
public class HealthController: Controller
{
[Authorize("check:health")]
[HttpGet]
public IActionResult Index() {
return Ok("healthy");
}
}
Steps
- Remove the scope/permission from the
[Authorize]annotation. RESULT: 200 OK returned as expected - Move
context.Succeed(requirement);andreturn Task.CompletedTaskto the top ofHandleRequirementAsyncmethod. RESULT 200 OK response returned as expected.