2

At the API level, I have some identity claims coming from Identity-Server for authorizing the current user and I could use some for first level of authorization. But in a next step, for additional permissions checks, these claims are not enough, I'll have to retrieve more data from database.

My idea was to create a custom ClaimsPrincipal where I attach all the permissions from database, but I don't want to call database more than once per request.

So, I thought of having my own implementation of Microsoft.AspNetCore.Authentication.IClaimsTransformer which I added to the pipeline like so:

app.UseClaimsTransformation(new ClaimsTransformationOptions() 
{
   Transformer = new MyClaimsTransformer() 
});

and my current transformation method:

public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
    if (context.Principal.Identity.IsAuthenticated)
    {
        var principal = new MyClaimsPrincipal(context.Principal);
        principal.Permissions = /* load from db */;
        return Task.FromResult<ClaimsPrincipal>(principal);
     }
     return Task.FromResult(context.Principal);
}

I already noticed that claims transformation method is called every time, with each request. So, I would like to have control over it, to avoid calling the database when there is no authenticated user or no authorization required (case of AllowAnonymous attribute) and when there is no need for extra permissions check (ideally).

I feel that the claims transformation should be bound to Identity Server's authorization or something, but no idea how.

Any thoughts on this? Thank you!

Edit I found here that I could hook the claims transformation to OnTokenValidated part of JwtBearerEvents from IdentityServer4.AccessTokenValidation. Unfortunately, there is another unknown middleware overwriting MyClaimsPrincipal. I am puzzled on how this works..

1 Answer 1

2

You can use the following check in your ClaimsTransformer to test for anonymous access.

using System.Security.Claims;
using System.Web;
using System.Web.Security;

private bool IsAnonymousAccessAllowed => UrlAuthorizationModule.CheckUrlAccessForPrincipal(
    HttpContext.Current.Request.Path, 
    AnonymousClaimsPrincipal, 
    HttpContext.Current.Request.RequestType
);

with AnonymousClaimsPrincipal as

ClaimsPrincipal AnonymousClaimsPrincipal => new ClaimsPrincipal(
    (IIdentity) new ClaimsIdentity(
        (IEnumerable<Claim>) new List<Claim>() {
             new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "")
        }
    )
);

Change the logic in the ClaimsTransformer to skip transformation for anonymous access:

if (!IsAnonymousAccessAllowed && context.Principal.Identity.IsAuthenticated) { /* ... */ }

(This code is from a MVC5 project, I hope it also works in .net core.)

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

3 Comments

This is useful.. But I actually found the best place to hook the claims transformation to (see edit).. Although I cannot create my own ClaimsPrincipal there, I am thinking to create my own ClaimsIdentity to attach the extra data to
@Learner you can easily add an extensionmethod to ClaimsPrincipal or ClaimsIdentity instead of providing your own Identity class
@Quetzalcoatl But I wanted to attach a complete object, not only extra claims, so I managed to create my own identity, add it to the Identities and then I performed checks against this object in the authorization handlers

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.