Refactor auth to AuthHex

This commit is contained in:
2026-08-25 16:46:05 +05:30
parent 916a2b8f0a
commit 97a1db1343
15 changed files with 561 additions and 81 deletions
+42 -4
View File
@@ -1,4 +1,4 @@
using System.Text;
using System.Security.Claims;
using Distribution.Data;
using Distribution.Features.Auth;
using Distribution.Features.Brand;
@@ -117,7 +117,11 @@ builder.Services.AddScoped<PaymentService>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
builder.Services.AddScoped<IJwtTokenService, JwtTokenService>();
builder.Services.AddHttpClient<IAuthHexClient, AuthHexClient>(client =>
{
var baseUrl = builder.Configuration["AuthHex:BaseUrl"] ?? throw new InvalidOperationException("AuthHex:BaseUrl is not configured.");
client.BaseAddress = new Uri(baseUrl);
});
builder.Services.AddScoped<IPermissionRepository, PermissionRepository>();
builder.Services.AddScoped<IScopeRepository, ScopeRepository>();
builder.Services.AddScoped<IPermissionService, PermissionService>();
@@ -126,7 +130,8 @@ builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
var jwtSection = builder.Configuration.GetSection("Jwt");
var jwtKey = jwtSection["Key"] ?? throw new InvalidOperationException("Jwt:Key is not configured.");
var jwtPublicKeyXml = jwtSection["PublicKey"] ?? throw new InvalidOperationException("Jwt:PublicKey is not configured.");
var jwtSigningKey = new RsaSecurityKey(RsaXmlKeyReader.ReadPublicKey(jwtPublicKeyXml));
builder.Services.AddAuthentication(options =>
{
@@ -142,10 +147,43 @@ builder.Services.AddAuthentication(options =>
ValidateAudience = true,
ValidAudience = jwtSection["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
IssuerSigningKey = jwtSigningKey,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
// AuthHex issues the token and owns its claim shape; this app's authorization (permission
// policies, role-level checks) runs against the *local* Role/Permission tables, so every
// validated token is mapped back to a local User/Role once here, not duplicated per
// endpoint. A token for a user with no matching local User record is rejected outright —
// login via AuthHex does not implicitly provision a local user.
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var subClaim = context.Principal?.FindFirst("sub")?.Value
?? context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (!Guid.TryParse(subClaim, out var userId))
{
context.Fail("Token is missing a valid user id claim.");
return;
}
var userRepo = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
var user = await userRepo.GetByIdAsync(userId);
if (user is null || !user.IsActive)
{
context.Fail("User not found or inactive.");
return;
}
var identity = (ClaimsIdentity)context.Principal!.Identity!;
identity.AddClaim(new Claim("username", user.Username));
identity.AddClaim(new Claim("role_id", user.FK_RoleId.ToString()));
identity.AddClaim(new Claim("role_level", (user.Role?.Level ?? 0).ToString()));
}
};
});
builder.Services.AddAuthorization();