Reputation: 141
I need to implement custom authorization based on some information in on-premise Active Directory. After doing some research, I figured that best approach would be to write a custom Authentication Filter and add that information from AD to the list of claims.
So after users are authenticated by IIS using Windows Authentication, I plan to read some information and put that among the list of claims:
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, userPrincipal.Name));
claims.Add(new Claim(ClaimTypes.Role, "client"));
claims.Add(new Claim("Accounts", "[List of accounts from AD]"));
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(new[] { identity });
context.Principal = principal;
Thread.CurrentPrincipal = context.Principal;
}
I believe that this approach will allow me to access the list of accounts from any controller. However, I am unable to add my IAuthenticationFilter
implementation to the list of global filters using the following approach.
builder.Services.AddControllers(config =>
{
config.Filters.Add(new ApiAuthenticationFilter())
});
This method required IFilterMetaData
interface, while I have implemented IAuthenticationFilter
. In previous Web API versions, we were able to access HttpConfiguration
in Application_Start()
method, but in ASP.NET Core 6 Web API, I am unable to find a way to add my filter to HttpConfiguration
.
Could you please tell me if that's the right approach, or I should try implementing the IActionFilter
interface? Or a different approach altogether.
Thanks!
Upvotes: 1
Views: 3527
Reputation: 1274
A little bit late, but I've implemented a complete implementation of IClaimsTransformation for both MinimalApis and ControllerBased APIs for you https://github.com/amiru3f/remote-claim-transformation
Please note that ClaimsTransofmer is not responsible for authorization/authentication. It's just responsible to fill up your claims and you can ensure if the User has required claims or not
Upvotes: 0
Reputation: 141
I figured IAuthenticationFilter
is not the right approach anymore to add Claims to the ClaimsPrincipal
. There's another interface IClaimsTransformation
that does exactly what I wanted.
The example posted on ASP.NET website was creating a new ClaimsIdentity
instance to the ClaimsPrincipal
while I was able to achieve that by just adding to existing list of claims:
public class MyClaimsTransformation : IClaimsTransformation
{
private readonly IActiveDirectoryService _activeDirectoryService;
public MyClaimsTransformation(IActiveDirectoryService activeDirectoryService)
{
_activeDirectoryService = activeDirectoryService;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var identity = principal.Identities.FirstOrDefault(c => c.IsAuthenticated);
if (identity == null)
{
return principal;
}
var user = _activeDirectoryService.GetUser(principal.Identity as WindowsIdentity);
// Add or replace identity.Claims
if (!principal.HasClaim(c => c.Type == MyClaimTypes.ACCOUNTS))
{
identity.AddClaim(new Claim(MyClaimTypes.ACCOUNTS, user.Accounts));
}
return principal;
}
}
Another advantage of using IClaimsTransformation
is to be able to inject dependencies through .Net core DI container.
In my Program.cs, I was unable to make it work using the recommended Authentication method:
builder.Services.AddAuthentication(IISDefaults.AuthenticationScheme);
However, using Negotiate worked for me:
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
Finally, add our custom IClaimsTranformation
implementation to services:
// Claim transformation
builder.Services.AddScoped<IClaimsTransformation, MyClaimsTransformation>();
In the past, it had to be done inside ConfigureServices()
method
More details about IClaimsTransformation are available here: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/claims?view=aspnetcore-6.0
Upvotes: 3