I have a user base with different claims to represent their plans. I want to strip some fields from my objects based on their plans.
I found an answer here: ASP.NET WebAPI Conditional Serialization based on User Role
But it is for HttpClient
only, since I can't use DelegatingHandler
in ASP.NET Core 8 (or don't understand how to) - is there any similar way to manipulate the response of all of my controllers?
According to your requirement, I implement this feature in my side. Here is the detailed steps.
ClaimBasedJsonConverter.cs
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json;
namespace WebApplicationApi
{
public class ClaimBasedJsonConverter : JsonConverter<object>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ClaimBasedJsonConverter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.GetProperties().Any(prop => prop.IsDefined(typeof(ClaimSensitiveAttribute), true));
}
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
var httpContext = _httpContextAccessor.HttpContext;
var userClaims = httpContext.User.Claims;
writer.WriteStartObject();
foreach (var property in value.GetType().GetProperties())
{
var propertyValue = property.GetValue(value);
var claimSensitiveAttribute = property.GetCustomAttribute<ClaimSensitiveAttribute>();
if (claimSensitiveAttribute == null)
{
writer.WritePropertyName(property.Name);
JsonSerializer.Serialize(writer, propertyValue, property.PropertyType, options);
continue;
}
var hasRequiredClaim = userClaims.Any(c =>
c.Type == claimSensitiveAttribute.ClaimType &&
claimSensitiveAttribute.ClaimValues.Contains(c.Value));
if (hasRequiredClaim)
{
writer.WritePropertyName(property.Name);
JsonSerializer.Serialize(writer, propertyValue, property.PropertyType, options);
}
}
writer.WriteEndObject();
}
}
}
ClaimSensitiveAttribute.cs
namespace WebApplicationApi
{
[AttributeUsage(AttributeTargets.Property)]
public class ClaimSensitiveAttribute : Attribute
{
public string ClaimType { get; }
public string[] ClaimValues { get; }
public ClaimSensitiveAttribute(string claimType, params string[] claimValues)
{
ClaimType = claimType;
ClaimValues = claimValues;
}
}
}
Register it
using WebApplicationApi;
var builder = WebApplication.CreateBuilder(args);
// Register it
builder.Services.AddHttpContextAccessor();
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new ClaimBasedJsonConverter(builder.Services.BuildServiceProvider()?.GetRequiredService<IHttpContextAccessor>()));
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// Make sure you have this line
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Apply it in model
namespace WebApplicationApi.Models
{
public class User
{
public string? Name { get; set; }
public string? Email { get; set; }
[ClaimSensitive("Plan", "Free", "Premium")]
public string? SensitiveData { get; set; }
[ClaimSensitive("Role", "Admin")]
public string? AdminNotes { get; set; }
}
}
Test it in controller
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using WebApplicationApi.Models;
namespace WebApplicationApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUser()
{
// If change Plan value to `Unknown`,
//{
// "Name": "John Doe",
// "Email": "[email protected]",
// "AdminNotes": "Admin Only Notes"
//}
var claims = new List<Claim>
{
new Claim("Plan", "Premium"),
new Claim("Role", "Admin")
};
var identity = new ClaimsIdentity(claims, "TestAuth");
var user = new ClaimsPrincipal(identity);
HttpContext.User = user;
var userModel = new User
{
Name = "John Doe",
Email = "[email protected]",
SensitiveData = "Sensitive Premium Info",
AdminNotes = "Admin Only Notes"
};
return Ok(userModel);
}
}
}
Test Result