auth clean up

This commit is contained in:
Kalana B. Thilakarathna
2026-08-25 16:11:37 +05:30
parent 916a2b8f0a
commit bb33928661
9 changed files with 2724 additions and 51 deletions
+17 -4
View File
@@ -2,14 +2,27 @@ using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Auth;
/// <summary>
/// Proxies all authentication traffic from the Next.js frontend to the
/// external Auth Service. The frontend must never call the Auth Service
/// directly — all auth goes through this controller.
///
/// TODO: Implement endpoints once Auth Service contract is agreed.
/// </summary>
[ApiController]
[Route("api/auth")]
public class AuthController(AuthService service) : ControllerBase
public class AuthController(IAuthServiceClient authServiceClient) : ControllerBase
{
// TODO: replace stub endpoints below with real proxy calls once
// the Auth Service API contract is finalised.
[HttpPost("login")]
public async Task<IActionResult> Login(LoginRequest request)
public Task<IActionResult> Login()
{
var result = await service.LoginAsync(request);
return result is null ? Unauthorized() : Ok(result);
// TODO: accept login request shape from Auth Service contract,
// forward to authServiceClient, relay response (token / session) back.
return Task.FromResult<IActionResult>(StatusCode(501, new { error = "Auth integration not yet implemented." }));
}
// TODO: add further endpoints as needed (refresh, logout, etc.)
}
+3 -3
View File
@@ -1,5 +1,5 @@
namespace Distribution.Features.Auth;
public record LoginRequest(string Username, string Password);
public record LoginResponse(string Token, Guid UserId, string Username, string RoleName);
// TODO: Define request/response shapes once the Auth Service contract is agreed.
// These will be the DTOs passed between the Next.js frontend and this proxy controller,
// which should mirror (or adapt) whatever the Auth Service expects/returns.
+4 -22
View File
@@ -1,24 +1,6 @@
using Distribution.Features.User;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Identity;
namespace Distribution.Features.Auth;
public class AuthService(IUserRepository userRepo, IJwtTokenService tokenService)
{
private readonly PasswordHasher<Models.User> _hasher = new();
public async Task<LoginResponse?> LoginAsync(LoginRequest request)
{
var user = await userRepo.GetByUsernameAsync(request.Username);
if (user is null || !user.IsActive)
return null;
var result = _hasher.VerifyHashedPassword(user, user.PasswordHash, request.Password);
if (result == PasswordVerificationResult.Failed)
return null;
var token = tokenService.GenerateToken(user);
return new LoginResponse(token, user.Id, user.Username, user.Role?.Name ?? string.Empty);
}
}
// TODO: This service previously contained local password validation logic.
// It has been replaced by the IAuthServiceClient connector which proxies
// to the external Auth Service. Remove this file or repurpose it for any
// local orchestration logic once the Auth Service contract is defined.
@@ -0,0 +1,20 @@
namespace Distribution.Features.Auth;
/// <summary>
/// Typed HttpClient that proxies requests to the external Auth Service.
/// Registered via AddHttpClient&lt;AuthServiceClient&gt; in Program.cs.
/// BaseAddress is configured from AuthService:BaseUrl in appsettings.json.
///
/// TODO: Implement methods once Auth Service contract is agreed.
/// </summary>
public class AuthServiceClient(HttpClient http) : IAuthServiceClient
{
// TODO: implement proxy methods, e.g.:
//
// public async Task<AuthServiceResponse?> LoginAsync(LoginRequest request, CancellationToken ct)
// {
// var response = await http.PostAsJsonAsync("/TODO_AUTH_SERVICE_LOGIN_PATH", request, ct);
// if (!response.IsSuccessStatusCode) return null;
// return await response.Content.ReadFromJsonAsync<AuthServiceResponse>(ct);
// }
}
@@ -0,0 +1,11 @@
namespace Distribution.Features.Auth;
public class AuthServiceOptions
{
public const string SectionName = "AuthService";
/// <summary>Base URL of the external Auth Service, e.g. "https://auth.internal".</summary>
public string BaseUrl { get; set; } = string.Empty;
// TODO: add other config (API keys, timeout, etc.) once Auth Service is defined.
}
@@ -0,0 +1,16 @@
namespace Distribution.Features.Auth;
/// <summary>
/// Connector to the external Auth Service. This backend proxies all auth
/// traffic from the Next.js frontend through to the Auth Service — the
/// frontend never calls the Auth Service directly.
///
/// TODO: Fill in the method signatures once the Auth Service contract is agreed.
/// </summary>
public interface IAuthServiceClient
{
// TODO: define methods based on Auth Service contract, e.g.:
// Task<AuthServiceResponse?> LoginAsync(LoginRequest request, CancellationToken ct);
// Task<AuthServiceResponse?> RefreshAsync(string refreshToken, CancellationToken ct);
// Task LogoutAsync(string token, CancellationToken ct);
}
+49 -22
View File
@@ -91,7 +91,6 @@ builder.Services.AddScoped<OrderService>();
builder.Services.AddScoped<SaleService>();
builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<RoleService>();
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<CategoryService>();
builder.Services.AddScoped<BrandService>();
builder.Services.AddScoped<UomService>();
@@ -117,7 +116,6 @@ builder.Services.AddScoped<PaymentService>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
builder.Services.AddScoped<IJwtTokenService, JwtTokenService>();
builder.Services.AddScoped<IPermissionRepository, PermissionRepository>();
builder.Services.AddScoped<IScopeRepository, ScopeRepository>();
builder.Services.AddScoped<IPermissionService, PermissionService>();
@@ -125,28 +123,57 @@ builder.Services.AddScoped<IScopeAccessService, ScopeAccessService>();
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.");
// ── Auth Service connector ───────────────────────────────────────────────────
// TODO: replace placeholder BaseAddress with real Auth Service URL once decided.
// The AuthServiceOptions:BaseUrl value in appsettings.json drives this.
builder.Services.Configure<AuthServiceOptions>(
builder.Configuration.GetSection(AuthServiceOptions.SectionName));
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
var authServiceBaseUrl = builder.Configuration[$"{AuthServiceOptions.SectionName}:BaseUrl"];
builder.Services.AddHttpClient<AuthServiceClient>(client =>
{
// TODO: set BaseAddress once Auth Service URL is known.
if (!string.IsNullOrWhiteSpace(authServiceBaseUrl))
client.BaseAddress = new Uri(authServiceBaseUrl);
});
builder.Services.AddScoped<IAuthServiceClient, AuthServiceClient>();
// ── Authentication ───────────────────────────────────────────────────────────
// TODO: replace this JWT configuration with the token validation strategy
// dictated by the Auth Service (symmetric key, JWKS/OIDC, etc.) once decided.
var jwtSection = builder.Configuration.GetSection("Jwt");
var jwtKey = jwtSection["Key"];
if (!string.IsNullOrWhiteSpace(jwtKey))
{
builder.Services.AddAuthentication(options =>
{
ValidateIssuer = true,
ValidIssuer = jwtSection["Issuer"],
ValidateAudience = true,
ValidAudience = jwtSection["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
});
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtSection["Issuer"],
ValidateAudience = true,
ValidAudience = jwtSection["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
});
}
else
{
// Auth Service integration not yet configured — all [Authorize] checks will
// fail until the token validation strategy is wired up.
builder.Services.AddAuthentication();
}
builder.Services.AddAuthorization();
+3
View File
@@ -17,5 +17,8 @@
},
"StoreManagement": {
"UnverifiedKycOrderThreshold": 0
},
"AuthService": {
"BaseUrl": ""
}
}
File diff suppressed because it is too large Load Diff