In my scenario, I have a back-end repository from a web application calling a Web API resource using the Machine-to-machine (Client Credentials) flow. The authentication part works fine – the respository is able to get an access token from Auth0 and the API accepts the request. Now I am trying to authorize the Web API endpoint with a scope, which fails.
I followed the instructions in the Auth0 documentation to register an application for the web application, create an API, and create a scope in the API which the application is authorized to use.
In the repository, I make the request with the token:
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, $"v2/staff/{username}");
var m2mToken = (await _auth0.GetM2MToken()).ToDto(); // Auth0 machine-to-machine access token
request.Headers.Add("Access-Token", m2mToken.AccessToken);
var message = await _httpClient.SendAsync(request);
On the API end, I added a policy rule to enforce the scope authorization:
var auth0Section = Configuration.GetSection("Auth0").Get<Dictionary<string, string>>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.Authority = auth0Section["Domain"];
options.Audience = auth0Section["Audience"];
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = ClaimTypes.NameIdentifier
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("access_as_user", policy => policy.Requirements.Add(new HasScopeRequirement("access_as_user", auth0Section["Domain"])));
});
services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
Then I created the scope requirement and handler classes.
public class HasScopeHandler : AuthorizationHandler<HasScopeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement)
{
// If user does not have the scope claim, get out of here
if (!context.User.HasClaim(c => c.Type == "scope" && c.Issuer == requirement.Issuer))
return Task.CompletedTask;
// Split the scopes string into an array
var scopes = context.User.FindFirst(c => c.Type == "scope" && c.Issuer == requirement.Issuer)?.Value.Split(' ') ?? System.Array.Empty<string>();
// Succeed if the scope array contains the required scope
if (scopes.Any(s => s == requirement.Scope))
context.Succeed(requirement);
return Task.CompletedTask;
}
}
Finally, I set the endpoint to require the scope in question.
[Authorize(Policy = "access_as_user")]
[HttpGet("{username}")]
public async Task<Microsoft.AspNetCore.Mvc.ActionResult> GetStaffRecordByUsername(string username)
{
...
}
But in the HasScopeHandler, the context.User claims do not include any “Scope” type claims, so the requirement fails. Am I missing some configuration in Auth0 or my Web API to get the claims I need?
I should note that I’m running both ends in debug mode on my development machine, if it changes anything.