diff --git a/backend/src/Tnsu.ApprovalRoute.Api/appsettings.json b/backend/src/Tnsu.ApprovalRoute.Api/appsettings.json
index 3205e20..bb23562 100644
--- a/backend/src/Tnsu.ApprovalRoute.Api/appsettings.json
+++ b/backend/src/Tnsu.ApprovalRoute.Api/appsettings.json
@@ -9,8 +9,8 @@
"Postgres": "Host=localhost;Port=5435;Database=tnsu_approval_route;Username=tnsu_approval_route;Password=tnsu_approval_route"
},
"Entra": {
- "TenantId": "",
- "Audience": ""
+ "TenantId": "b4c2af35-bfd6-4cff-9af9-556efb53e65a",
+ "Audience": "9232d9e2-9807-4753-808f-07250e5474b0"
},
"Dev": {
"DefaultUserEmail": "dev@tnsu.kz"
diff --git a/backend/src/Tnsu.ApprovalRoute.Infrastructure/Tnsu.ApprovalRoute.Infrastructure.csproj b/backend/src/Tnsu.ApprovalRoute.Infrastructure/Tnsu.ApprovalRoute.Infrastructure.csproj
index 5ea6939..33b62bc 100644
--- a/backend/src/Tnsu.ApprovalRoute.Infrastructure/Tnsu.ApprovalRoute.Infrastructure.csproj
+++ b/backend/src/Tnsu.ApprovalRoute.Infrastructure/Tnsu.ApprovalRoute.Infrastructure.csproj
@@ -13,5 +13,7 @@
+
+
diff --git a/backend/src/Tnsu.ApprovalRoute.Infrastructure/TnsuPortal/HttpTnsuPortalClient.cs b/backend/src/Tnsu.ApprovalRoute.Infrastructure/TnsuPortal/HttpTnsuPortalClient.cs
index 621693d..d1b387e 100644
--- a/backend/src/Tnsu.ApprovalRoute.Infrastructure/TnsuPortal/HttpTnsuPortalClient.cs
+++ b/backend/src/Tnsu.ApprovalRoute.Infrastructure/TnsuPortal/HttpTnsuPortalClient.cs
@@ -1,14 +1,23 @@
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
+using Microsoft.AspNetCore.Http;
using Tnsu.ApprovalRoute.Domain.Enums;
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
-// Клиент api.tnsu.kz (api-tnsu-portal). Сервисный токен прокидывается через TnsuPortalTokenProvider.
-public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenProvider tokenProvider) : ITnsuPortalClient
+// Клиент api.tnsu.kz (api-tnsu-portal).
+public sealed class HttpTnsuPortalClient(
+ HttpClient httpClient,
+ TnsuPortalTokenProvider tokenProvider,
+ IHttpContextAccessor httpContextAccessor) : ITnsuPortalClient
{
+ // Разные эндпоинты api-tnsu-portal отдают JSON в разном регистре (Dapper-результаты — PascalCase,
+ // строго типизированные модели — camelCase), поэтому сопоставляем поля без учёта регистра.
+ private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
+
public async Task GetEmployeeByEmailAsync(string email, CancellationToken cancellationToken)
{
using var request = new HttpRequestMessage(HttpMethod.Get, $"/Employee/ByEmail?email={Uri.EscapeDataString(email)}");
@@ -19,7 +28,7 @@ public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenP
return null;
response.EnsureSuccessStatusCode();
- var employee = await response.Content.ReadFromJsonAsync(cancellationToken);
+ var employee = await response.Content.ReadFromJsonAsync(JsonOptions, cancellationToken);
if (employee is null)
return null;
@@ -36,7 +45,7 @@ public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenP
return [];
response.EnsureSuccessStatusCode();
- var project = await response.Content.ReadFromJsonAsync(cancellationToken);
+ var project = await response.Content.ReadFromJsonAsync(JsonOptions, cancellationToken);
if (project is null)
return [];
@@ -56,8 +65,18 @@ public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenP
executors.Add(new ProjectExecutorInfo(role, email));
}
+ // ВРЕМЕННО: для TnsuPortal ещё нет своего client-credentials приложения в Entra, поэтому прокидываем
+ // токен текущего пользователя дальше — его aud совпадает с ClientId api-tnsu-portal, так что подходит.
+ // Когда появится своё приложение — вернуть на tokenProvider.GetAccessTokenAsync(...).
private async Task AuthorizeAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
+ var incoming = httpContextAccessor.HttpContext?.Request.Headers.Authorization.ToString();
+ if (!string.IsNullOrWhiteSpace(incoming))
+ {
+ request.Headers.TryAddWithoutValidation("Authorization", incoming);
+ return;
+ }
+
var token = await tokenProvider.GetAccessTokenAsync(cancellationToken);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}