diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionCleanupWorker.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionCleanupWorker.cs index 1948c58..1a8a424 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionCleanupWorker.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionCleanupWorker.cs @@ -54,12 +54,6 @@ public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDep .Where(t => t.GetIsActive()) .ToList(); - logger.LogDebug( - "IdentitySession temizliği: host + {Count} aktif tenant işlenecek (eşik: {Cutoff:s}).", - activeTenants.Count, cutoff); - - var totalDeleted = 0; - // Host (null) + tüm aktif tenant'lar var scopes = Enumerable.Repeat(null, 1) .Concat(activeTenants.Select(t => (Guid?)t.Id)); @@ -76,29 +70,21 @@ public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDep foreach (var session in staleSessions) { await sessionRepo.DeleteAsync(session, cancellationToken: cancellationToken); - } - totalDeleted += staleSessions.Count; - - if (staleSessions.Count > 0) - { logger.LogInformation( - "IdentitySession temizliği: {Scope} için {Deleted} atıl oturum silindi.", - tenantId.HasValue ? $"Tenant [{tenantId}]" : "Host", staleSessions.Count); + "AbpSessions temizliği: TenantId:{TenantId}, UserId:{UserId}, SignedIn:{SignedIn}, Now:{Now}", + tenantId, + session.UserId, + session.SignedIn, + DateTime.UtcNow); } } } - if (totalDeleted > 0) - { - logger.LogInformation( - "IdentitySession temizliği tamamlandı: toplam {Deleted} oturum silindi.", - totalDeleted); - } } catch (Exception ex) { - logger.LogError(ex, "IdentitySession temizliği sırasında hata oluştu."); + logger.LogError(ex, "AbpSessions temizliği sırasında hata oluştu."); } } } diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionRevocationHandler.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionRevocationHandler.cs index 07c6e66..719040c 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionRevocationHandler.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSessionRevocationHandler.cs @@ -8,6 +8,7 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.MultiTenancy; using Volo.Abp.Security.Claims; +using System.Linq; namespace Sozsoft.Platform.Identity; @@ -20,14 +21,6 @@ public class PlatformSessionRevocationHandler : IOpenIddictServerHandler, ITransientDependency { - public static OpenIddictServerHandlerDescriptor Descriptor { get; } - = OpenIddictServerHandlerDescriptor - .CreateBuilder() - .UseScopedHandler() - .SetOrder(int.MaxValue - 10) - .SetType(OpenIddictServerHandlerType.Custom) - .Build(); - private readonly IIdentitySessionRepository sessionRepo; private readonly ICurrentTenant currentTenant; private readonly ILogger logger; @@ -51,12 +44,7 @@ public class PlatformSessionRevocationHandler : if (string.IsNullOrEmpty(userId) || !Guid.TryParse(userId, out var userGuid)) return; // Token'dan tenant ID'yi al. - var tenantIdStr = context.Principal?.GetClaim(AbpClaimTypes.TenantId); - Guid? tenantId = null; - if (!string.IsNullOrWhiteSpace(tenantIdStr) && Guid.TryParse(tenantIdStr, out var parsedTenantId)) - { - tenantId = parsedTenantId; - } + var tenantId = ParseTenantId(context.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId)?.Value); try { @@ -67,13 +55,13 @@ public class PlatformSessionRevocationHandler : foreach (var session in sessions) { await sessionRepo.DeleteAsync(session); - } - if (sessions.Count > 0) - { logger.LogInformation( - "Token revocation: {Count} IdentitySession silindi. UserId: {UserId}, TenantId: {TenantId}", - sessions.Count, userGuid, tenantId); + "AbpSessions temizliği: TenantId:{TenantId}, UserId:{UserId}, SignedIn:{SignedIn}, Now:{Now}", + tenantId, + session.UserId, + session.SignedIn, + DateTime.UtcNow); } } } @@ -81,7 +69,9 @@ public class PlatformSessionRevocationHandler : { // Session temizleme hatası revocation akışını bloklamamalı. logger.LogWarning(ex, - "Token revocation sonrası IdentitySession temizliğinde hata. UserId: {UserId}", userId); + "Token revocation sonrası AbpSessions temizliğinde hata. UserId: {UserId}", userId); } } + + private static Guid? ParseTenantId(string value) => !string.IsNullOrWhiteSpace(value) && Guid.TryParse(value, out var id) ? id : null; } diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformTokenController.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformTokenController.cs index 6a7edb6..fc5a491 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformTokenController.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformTokenController.cs @@ -253,12 +253,16 @@ Your login code: {twoFactorToken}"; if (existingSessions.Any()) { foreach (var session in existingSessions) + { await identitySessionRepository.DeleteAsync(session); + } } else { if (!await platformSignInManager.CheckConcurrentLimitAsync(user)) + { throw new UserFriendlyException(PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit); + } } await InsertSessionAsync(user, request.ClientId); @@ -267,7 +271,7 @@ Your login code: {twoFactorToken}"; catch (UserFriendlyException) { throw; } catch (Exception ex) { - Logger.LogWarning(ex, "IdentitySession yönetiminde hata oluştu. UserId: {UserId}", user.Id); + Logger.LogWarning(ex, "AbpSessions yönetiminde hata oluştu. UserId: {UserId}", user.Id); } } @@ -275,50 +279,46 @@ Your login code: {twoFactorToken}"; protected override async Task HandleRefreshTokenAsync(OpenIddictRequest request) { var info = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - var tenantId = ParseTenantId(info.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId)?.Value); + var userId = info.Principal?.GetClaim(OpenIddictConstants.Claims.Subject); IdentityUser refreshUser = null; - var userIdStr = info.Principal?.GetClaim(OpenIddictConstants.Claims.Subject); - if (!userIdStr.IsNullOrWhiteSpace() && Guid.TryParse(userIdStr, out var userId)) + + using (CurrentTenant.Change(tenantId)) { - using (CurrentTenant.Change(tenantId)) + if (!userId.IsNullOrWhiteSpace() && Guid.TryParse(userId, out var userGuid)) { - refreshUser = await UserManager.FindByIdAsync(userId.ToString()); + refreshUser = await UserManager.FindByIdAsync(userId); if (refreshUser != null) { + // Concurrent login limiti refresh token için de geçerlidir; yeni oturum açılmadan önce eskiler temizlenir. if (!await platformSignInManager.CheckConcurrentLimitAsync(refreshUser)) throw new UserFriendlyException(PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit); - var existingSessions = await identitySessionRepository.GetListAsync(userId: userId); + var existingSessions = await identitySessionRepository.GetListAsync(userId: userGuid); foreach (var session in existingSessions) + { await identitySessionRepository.DeleteAsync(session); + } } } - } - IActionResult result; - using (CurrentTenant.Change(tenantId)) - { - result = await base.HandleRefreshTokenAsync(request); - } + var result = await base.HandleRefreshTokenAsync(request); - if (result is Microsoft.AspNetCore.Mvc.SignInResult && refreshUser != null) - { - try + if (result is Microsoft.AspNetCore.Mvc.SignInResult && refreshUser != null) { - using (CurrentTenant.Change(refreshUser.TenantId)) + try { await InsertSessionAsync(refreshUser, request.ClientId); } + catch (Exception ex) + { + Logger.LogWarning(ex, "Refresh token sonrası IdentitySession oluşturulamadı. UserId: {UserId}", refreshUser.Id); + } } - catch (Exception ex) - { - Logger.LogWarning(ex, "Refresh token sonrası IdentitySession oluşturulamadı. UserId: {UserId}", refreshUser.Id); - } - } - return result; + return result; + } } private async Task InsertSessionAsync(IdentityUser user, string clientId) diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs index ad3f553..58bea6f 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs @@ -26,8 +26,10 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; +using OpenIddict.Server; using OpenIddict.Server.AspNetCore; using OpenIddict.Validation.AspNetCore; +using static OpenIddict.Server.OpenIddictServerEvents; using Volo.Abp; using Volo.Abp.Account.Web; using Volo.Abp.AspNetCore.Auditing; @@ -85,6 +87,8 @@ public class PlatformHttpApiHostModule : AbpModule { server.SetAccessTokenLifetime(TimeSpan.FromMinutes(60)); server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(90)); + server.AddEventHandler(builder => + builder.UseScopedHandler()); }); builder.AddValidation(options => {