feat: перелопатил синхронизацию преподавателей
Backend CI / build-and-test (push) Failing after 13m11s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Failing after 10m12s
Frontend CI / build-and-check (push) Failing after 16m9s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14m6s
🚀 Create and publish a Docker image / Build & publish backend image (push) Failing after 14m58s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Failing after 14m58s

This commit is contained in:
2026-05-24 21:06:03 +03:00
parent 6aef5dd66f
commit 99d25adbb1
33 changed files with 1756 additions and 90 deletions
@@ -1,4 +1,3 @@
using Microsoft.Identity.Client;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
@@ -23,6 +22,7 @@ public class AuthService : IAuthService
{
private readonly AppDbContext _db;
private readonly IConfiguration _config;
private readonly IMicrosoftAuthClient _microsoftAuth;
private readonly IGamificationService _gamification;
private readonly INotificationService _notifications;
private readonly ILogger<AuthService> _logger;
@@ -30,12 +30,14 @@ public class AuthService : IAuthService
public AuthService(
AppDbContext db,
IConfiguration config,
IMicrosoftAuthClient microsoftAuth,
IGamificationService gamification,
INotificationService notifications,
ILogger<AuthService> logger)
{
_db = db;
_config = config;
_microsoftAuth = microsoftAuth;
_gamification = gamification;
_notifications = notifications;
_logger = logger;
@@ -43,36 +45,10 @@ public class AuthService : IAuthService
public async Task<AuthResult> LoginWithMicrosoftAsync(string authorizationCode, string? redirectUri = null, string? ipAddress = null)
{
var tenantId = _config["AzureAd:TenantId"];
var clientId = _config["AzureAd:ClientId"];
var clientSecret = _config["AzureAd:ClientSecret"];
var instance = _config["AzureAd:Instance"] ?? "https://login.microsoftonline.com/";
if (string.IsNullOrWhiteSpace(tenantId) || string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret))
throw new UnauthorizedException("Аутентификация Microsoft не настроена (AzureAd:TenantId/ClientId/ClientSecret).");
var effectiveRedirectUri = redirectUri
?? _config["AzureAd:RedirectUri"]
?? "http://localhost:5173/auth/callback";
var authority = $"{instance.TrimEnd('/')}/{tenantId}";
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.WithRedirectUri(effectiveRedirectUri)
.Build();
AuthenticationResult result;
try
{
result = await app.AcquireTokenByAuthorizationCode(new[] { "User.Read" }, authorizationCode)
.ExecuteAsync();
}
catch (MsalException ex)
{
throw new UnauthorizedException($"Ошибка аутентификации Microsoft: {ex.Message}");
}
var result = await _microsoftAuth.ExchangeAuthorizationCodeAsync(authorizationCode, effectiveRedirectUri);
// Parse claims directly from the ID token provided by Microsoft
var handler = new JwtSecurityTokenHandler();
@@ -80,13 +56,21 @@ public class AuthService : IAuthService
var email = idToken.Claims.FirstOrDefault(c => c.Type == "preferred_username" || c.Type == "email" || c.Type == ClaimTypes.Upn)?.Value;
var name = idToken.Claims.FirstOrDefault(c => c.Type == "name")?.Value;
var microsoftSub = idToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Sub || c.Type == "sub")?.Value;
if (string.IsNullOrEmpty(email))
throw new UnauthorizedException("Email не найден в токене Microsoft.");
if (string.IsNullOrWhiteSpace(microsoftSub))
throw new UnauthorizedException("Sub ID не найден в токене Microsoft.");
// Automatically provision user
var user = await _db.Users
.Include(u => u.Roles)
.Include(u => u.TeacherProfile)
.FirstOrDefaultAsync(u => u.MicrosoftId == microsoftSub);
user ??= await _db.Users
.Include(u => u.Roles)
.Include(u => u.TeacherProfile)
.FirstOrDefaultAsync(u => u.Email == email);
if (user == null)
{
@@ -94,6 +78,7 @@ public class AuthService : IAuthService
{
Email = email,
DisplayName = name ?? email.Split('@')[0],
MicrosoftId = microsoftSub,
IsActive = true
};
_db.Users.Add(user);
@@ -107,6 +92,14 @@ public class AuthService : IAuthService
{
throw new ForbiddenException("Аккаунт деактивирован.");
}
else
{
user.Email = email;
user.DisplayName = name ?? user.DisplayName ?? email.Split('@')[0];
user.MicrosoftId = microsoftSub;
user.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
}
if (user.Roles.Count == 0)
{