1

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

  1. Do I need to setup a User and Roles in Auth0 for this to work?
  2. Is there additional code needed for dotnet core Policy-Based Authorization than the code I've provided below?
  3. 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

Working with Auth0 locally

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

  1. Remove the scope/permission from the [Authorize] annotation. RESULT: 200 OK returned as expected
  2. Move context.Succeed(requirement); and return Task.CompletedTask to the top of HandleRequirementAsync method. RESULT 200 OK response returned as expected.

1 Answer 1

2

Turns out all the code was right but there was a misconfiguration in the Auth0 tenant. Under Dashboard > APIs > Machine to Machine Applications > Your Client App > Scopes is where scopes get applied to a specific client.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.