1
votes

I'm creating a PWA with React and using Auth0 as my identity provider and JWT Bearer token as authentication. I am injecting roles into my JWT token so that the client-side can limit what options are available to the user and I have this working pretty well.

I want to now limit the server side so that an endpoint can't be called unless the user has the necessary role(s) required to access that endpoint.

Annoyingly, Auth0 doesn't appear to support adding in the roles or role claim that aspnet core seems to handle OOTB; it requires that a domain preface the roles in the claims definition. ie, https://bob.com/roles as the claim.

I'm trying to work out how to get the Authorize(Roles = "Administrator") attribute to honour the domain-prefaced claim for roles.

I have tried updating the Auth0 Rule to set the role or roles property but these never get returned; only the domain-prefaced roles claim seems to return.

I have found other info for more specific Authentication providers and they include a MapJsonKey extension on ClaimActions that look like they would fit the bill, but the standard AuthenticationOptions object in the AddAuthentication extension doesn't appear to have this.

My ConfigureServices in App.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            // 1. Add Authentication Services
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.Authority = Configuration["Auth0:Authority"];
                options.Audience = Configuration["Auth0:ClientId"];
            });

            // In production, the React files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });
        }

My Rule for injecting the roles into the JWT in Auth0:

function (user, context, callback) {
  const namespace = 'http://bob.com';
  const assignedRoles = (context.authorization || {}).roles;

  let idTokenClaims = context.idToken || {};
  let accessTokenClaims = context.accessToken || {};

  idTokenClaims[`roles`] = assignedRoles; // This was an attempt to set the roles in 'roles' but doesn't get returned.
  accessTokenClaims[`roles`] = assignedRoles;

  idTokenClaims[`${namespace}/roles`] = assignedRoles; // This does get returned
  accessTokenClaims[`${namespace}/roles`] = assignedRoles;

  context.idToken = idTokenClaims;
  context.accessToken = accessTokenClaims;
  callback(null, user, context);
}

Example JWT Payload

{
    "http://bob.com/roles": [
        "Administrator"
    ],
    "given_name": "Name",
    "iss": "{issuer}",
    "sub": "{subject}",
    "aud": "{audience}"
}

asp.net core Action (taken from the example project, but with auth added)

[HttpGet("[action]"), Authorize(Roles = "Administrator")]
        public IEnumerable<WeatherForecast> WeatherForecasts()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            });
        }

What I would like is to be able to either map the http://bob.com/roles to roles, get the aspnet core Authorize attribute to look at the http://bob.com/roles, or get Auth0 to be able to return the roles in a 'roles' object.

Where I got the MapJsonKey info from: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/additional-claims?view=aspnetcore-2.2

Using Roles with the ASP.NET Core JWT middleware https://www.jerriepelser.com/blog/using-roles-with-the-jwt-middleware/

1

1 Answers

2
votes

For anyone who finds this, I found a solution to this. If you update the JWT claim to be http://schemas.microsoft.com/ws/2008/06/identity/claims/role then it all works straight away.

Updated Auth0 Rule

function (user, context, callback) {
  const assignedRoles = (context.authorization || {}).roles;

  let idTokenClaims = context.idToken || {};
  let accessTokenClaims = context.accessToken || {};

  idTokenClaims[`http://schemas.microsoft.com/ws/2008/06/identity/claims/role`] = assignedRoles;
  accessTokenClaims[`http://schemas.microsoft.com/ws/2008/06/identity/claims/role`] = assignedRoles;

  context.idToken = idTokenClaims;
  context.accessToken = accessTokenClaims;
  callback(null, user, context);
}