авторизация готова

This commit is contained in:
gorbenko.pavel
2026-07-23 14:14:44 +05:00
parent 1a5ed34182
commit f2a448d985
10 changed files with 113 additions and 0 deletions
@@ -0,0 +1,6 @@
namespace Tnsu.ApprovalRoute.Api.Auth;
public static class AuthSchemes
{
public const string Dev = "Dev";
}
@@ -0,0 +1,41 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
namespace Tnsu.ApprovalRoute.Api.Auth;
public static class AuthSetup
{
public static IServiceCollection AddApprovalRouteAuth(this IServiceCollection services, IConfiguration configuration)
{
var tenantId = configuration["Entra:TenantId"];
var audience = configuration["Entra:Audience"];
if (!string.IsNullOrWhiteSpace(tenantId) && !string.IsNullOrWhiteSpace(audience))
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers =
[
$"https://login.microsoftonline.com/{tenantId}/v2.0",
$"https://sts.windows.net/{tenantId}/"
],
ValidAudiences = [audience, $"api://{audience}"]
};
});
}
else
{
services.AddAuthentication(AuthSchemes.Dev)
.AddScheme<AuthenticationSchemeOptions, DevAuthHandler>(AuthSchemes.Dev, _ => { });
}
services.AddAuthorization();
return services;
}
}
@@ -0,0 +1,11 @@
using System.Security.Claims;
using Tnsu.ApprovalRoute.Application.Common.Interfaces;
namespace Tnsu.ApprovalRoute.Api.Auth;
public sealed class CurrentUserAccessor(IHttpContextAccessor httpContextAccessor) : ICurrentUser
{
public string? Email =>
httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.Email)
?? httpContextAccessor.HttpContext?.User.FindFirstValue("preferred_username");
}
@@ -0,0 +1,27 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
namespace Tnsu.ApprovalRoute.Api.Auth;
// Используется только когда Entra:TenantId/Entra:Audience не заданы (локальная разработка без реального Entra ID).
public sealed class DevAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
IConfiguration configuration)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var email = Request.Headers["X-Dev-User-Email"].FirstOrDefault()
?? configuration["Dev:DefaultUserEmail"]
?? "dev@tnsu.kz";
var identity = new ClaimsIdentity([new Claim(ClaimTypes.Email, email)], AuthSchemes.Dev);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), AuthSchemes.Dev);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
@@ -1,4 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Tnsu.ApprovalRoute.Api.Common; using Tnsu.ApprovalRoute.Api.Common;
using Tnsu.ApprovalRoute.Application.RequestTypes; using Tnsu.ApprovalRoute.Application.RequestTypes;
@@ -6,6 +7,7 @@ using Tnsu.ApprovalRoute.Application.RequestTypes;
namespace Tnsu.ApprovalRoute.Api.Controllers; namespace Tnsu.ApprovalRoute.Api.Controllers;
[ApiController] [ApiController]
[Authorize]
[Route("api/request-types")] [Route("api/request-types")]
public sealed class RequestTypesController(ISender mediator) : ControllerBase public sealed class RequestTypesController(ISender mediator) : ControllerBase
{ {
@@ -1,4 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Tnsu.ApprovalRoute.Api.Common; using Tnsu.ApprovalRoute.Api.Common;
using Tnsu.ApprovalRoute.Application.TemplateRouteSteps; using Tnsu.ApprovalRoute.Application.TemplateRouteSteps;
@@ -6,6 +7,7 @@ using Tnsu.ApprovalRoute.Application.TemplateRouteSteps;
namespace Tnsu.ApprovalRoute.Api.Controllers; namespace Tnsu.ApprovalRoute.Api.Controllers;
[ApiController] [ApiController]
[Authorize]
[Route("api/template-routes/{routeId:guid}/steps")] [Route("api/template-routes/{routeId:guid}/steps")]
public sealed class TemplateRouteStepsController(ISender mediator) : ControllerBase public sealed class TemplateRouteStepsController(ISender mediator) : ControllerBase
{ {
@@ -1,4 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Tnsu.ApprovalRoute.Api.Common; using Tnsu.ApprovalRoute.Api.Common;
using Tnsu.ApprovalRoute.Application.TemplateRoutes; using Tnsu.ApprovalRoute.Application.TemplateRoutes;
@@ -6,6 +7,7 @@ using Tnsu.ApprovalRoute.Application.TemplateRoutes;
namespace Tnsu.ApprovalRoute.Api.Controllers; namespace Tnsu.ApprovalRoute.Api.Controllers;
[ApiController] [ApiController]
[Authorize]
[Route("api/template-routes")] [Route("api/template-routes")]
public sealed class TemplateRoutesController(ISender mediator) : ControllerBase public sealed class TemplateRoutesController(ISender mediator) : ControllerBase
{ {
@@ -3,13 +3,18 @@ using Extensions.Hosting.AsyncInitialization;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Tnsu.ApprovalRoute.Api.Auth;
using Tnsu.ApprovalRoute.Application; using Tnsu.ApprovalRoute.Application;
using Tnsu.ApprovalRoute.Application.Common.Interfaces;
using Tnsu.ApprovalRoute.Infrastructure; using Tnsu.ApprovalRoute.Infrastructure;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApprovalRouteInfrastructure(builder.Configuration); builder.Services.AddApprovalRouteInfrastructure(builder.Configuration);
builder.Services.AddApprovalRouteApplication(); builder.Services.AddApprovalRouteApplication();
builder.Services.AddApprovalRouteAuth(builder.Configuration);
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUser, CurrentUserAccessor>();
builder.Services.AddOpenApi(); builder.Services.AddOpenApi();
builder.Services.AddHealthChecks(); builder.Services.AddHealthChecks();
builder.Services.AddControllers() builder.Services.AddControllers()
@@ -21,6 +26,10 @@ if (app.Environment.IsDevelopment())
app.MapOpenApi(); app.MapOpenApi();
app.MapHealthChecks("/health"); app.MapHealthChecks("/health");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers(); app.MapControllers();
await app.InitAndRunAsync(); await app.InitAndRunAsync();
@@ -7,5 +7,12 @@
}, },
"ConnectionStrings": { "ConnectionStrings": {
"Postgres": "Host=localhost;Port=5435;Database=tnsu_approval_route;Username=tnsu_approval_route;Password=tnsu_approval_route" "Postgres": "Host=localhost;Port=5435;Database=tnsu_approval_route;Username=tnsu_approval_route;Password=tnsu_approval_route"
},
"Entra": {
"TenantId": "",
"Audience": ""
},
"Dev": {
"DefaultUserEmail": "dev@tnsu.kz"
} }
} }
@@ -0,0 +1,6 @@
namespace Tnsu.ApprovalRoute.Application.Common.Interfaces;
public interface ICurrentUser
{
string? Email { get; }
}