0

I have followed some guides from MS and others on protecting a .Net core 7 controller based API. I have created roles inside the app registration and assigned users to those roles. Inside Program.cs I have generated some policies I plan to use, however, regardless of whether I check for policy or role on the API controller, I always get a 403. The 403 returns both inside Swagger when testing the API and in postman when calling the API.

I have confirmed the MS Identity bearer token in JWT.io and everything is correct including signature, and I confirm the expected roles are listed for the token gatherer. I think I am confusing myself as I am taking advice from multiple sources.

In my head, and please correct if I am wrong, the benefit of using Azure AD for a single tenant API is when called (eventually from a separate web app) the web app ensures the user is authenticated, gathers a token and then sends it in the request to the API. If the user has roles these are sent in the token. Using the authorize decorators on the web API, the API would check for the existence of these roles in the token and grant or deny access accordingly. I may be completely over-simplifying the process, but from what the docs have said that is the process I envisage.

Here is a portion of the Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))    
        .EnableTokenAcquisitionToCallDownstreamApi()
            .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
            .AddInMemoryTokenCaches();
builder.Services.AddControllers();
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters.RoleClaimType = "roles";
});
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy => policy.RequireRole("Admin.Senior","HR.Manager","Payroll.Manager"));
    options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin.Senior"));
    options.AddPolicy("RequireManagerRole", policy => policy.RequireRole("Branch.Manager","Operations.Manager"));
});
... unrelated others
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

So regardless of whether I use

[Route("api/[controller]")]
[ApiController]
[Authorize(Policy = "ElevatedRights")]
public class ColleaguesController : ControllerBase
{
    // GET: api/<ColleaguesController>
    [HttpGet]
    public async Task<IResult> Get(IColleagueData data)
    {
        try
        {
            return Results.Ok(await data.GetColleagues());
        }
        catch (Exception ex)
        {
            return Results.Problem(ex.Message);
        }
    }

or

[Route("api/[controller]")]
[ApiController]
[Authorize(Roles = "Admin.Senior")]
public class BranchesController : ControllerBase
{
    // GET: api/<BranchesController>
    [HttpGet]    
    public async Task<IResult> Get(IBranchData data)
    {
        try
        {
            return Results.Ok(await data.GetBranches());
        }
        catch (Exception ex)
        {
            return Results.Problem(ex.Message);
        }
        
    }

I get a 403 response.

I am sure it is me not understanding something as opposed to it being misconfigured, but I would be really grateful for any insight. As mentioned before I can confirm that the logged in user has the role Admin.Senior and that value is passed inside the token by the key "roles". If it helps confirm the API is configured correctly, I can add [AllowAnonymous] to a method e.g. Get A Branch (as opposed to all) and that returns the data with a 200.

EDIT

Token added in web app using MSAL library

string[] scopes = new string[] {"api://6d93b2cc-6928-4123-a27e-XXXXXXXXX/access_as_user" };
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
var client = _clientFactory.CreateClient("davidsons");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

Roles in Bearer token

"roles": [
    "Admin.Senior"
  ],
  • How do you put the bearer token while requesting the API? Do you have any request details when you got 403 error? – hina10531 Mar 15 '23 at 07:11
  • @hina10531 Inside Swagger, it is added via the UI. In Postman it is added in the authorization tab and passed in the Header. When called in a web app the token is added to the Header via the client factory extension methods. Could you explain what you refer to as request details? Obviously, most information is in the Header, but are you asking if I pass anything in the body? If so the answer is no, If I for instance wanted to get a single branch, there is an endpoint prepared to just accept it in the URL e.g. https://...../branches/15 – Colin-G-Davidson Mar 15 '23 at 09:08
  • Hi @Colin-G-Davidson, 403 means the user which is logged in does not contain the correct role. How do you generate the jwt token? – Rena Mar 15 '23 at 09:50
  • Please post the roles part of your bearer token. – Maxim Zabolotskikh Mar 15 '23 at 10:28
  • @Rena I have edited the question, as the comments would not format the code well. – Colin-G-Davidson Mar 15 '23 at 13:35

0 Answers0