Reputation: 3396
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..
Upvotes: 1
Views: 2414
Reputation: 9463
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.)
Upvotes: 2