using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Mirea.Api.Security.Common.Domain; using Mirea.Api.Security.Common.Interfaces; using Mirea.Api.Security.Services; using System; using System.Collections.Generic; using System.Text; namespace Mirea.Api.Security; public static class DependencyInjection { private static ReadOnlyMemory<byte> NormalizeKey(string key, int requiredLength) { var keyBytes = Encoding.UTF8.GetBytes(key); if (keyBytes.Length < requiredLength) { var normalizedKey = new byte[requiredLength]; Array.Copy(keyBytes, normalizedKey, keyBytes.Length); return new ReadOnlyMemory<byte>(normalizedKey); } if (keyBytes.Length > requiredLength) Array.Resize(ref keyBytes, requiredLength); return new ReadOnlyMemory<byte>(keyBytes); } public static IServiceCollection AddSecurityServices(this IServiceCollection services, IConfiguration configuration) { var saltSize = int.Parse(configuration["SECURITY_SALT_SIZE"]!); var hashSize = int.Parse(configuration["SECURITY_HASH_SIZE"]!); var iteration = int.Parse(configuration["SECURITY_HASH_ITERATION"]!); var memory = int.Parse(configuration["SECURITY_HASH_MEMORY"]!); var parallelism = int.Parse(configuration["SECURITY_HASH_PARALLELISM"]!); services.AddSingleton(new PasswordHashService { SaltSize = saltSize, HashSize = hashSize, Iterations = iteration, Memory = memory, Parallelism = parallelism, Secret = configuration["SECURITY_HASH_TOKEN"] }); var lifeTimeRefreshToken = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_RT"]!)); var lifeTimeFirstAuthToken = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_1_FA"]!)); services.AddSingleton(provider => { var cacheService = provider.GetRequiredService<ICacheService>(); var accessTokenService = provider.GetRequiredService<IAccessToken>(); var revokedTokenService = provider.GetRequiredService<IRevokedToken>(); var logger = provider.GetRequiredService<ILogger<AuthService>>(); var passwordService = provider.GetRequiredService<PasswordHashService>(); return new AuthService(cacheService, accessTokenService, revokedTokenService, logger, passwordService) { Lifetime = lifeTimeRefreshToken, LifetimeFirstAuth = lifeTimeFirstAuthToken }; }); var providers = new Dictionary<OAuthProvider, (string ClientId, string Secret)>(); foreach (var provider in Enum.GetValues<OAuthProvider>()) { var providerName = Enum.GetName(provider)!.ToUpper(); var clientId = configuration[$"{providerName}_CLIENT_ID"]; var secret = configuration[$"{providerName}_CLIENT_SECRET"]; if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(secret)) continue; providers.Add(provider, (clientId, secret)); } services.AddSingleton(provider => new OAuthService( provider.GetRequiredService<ILogger<OAuthService>>(), providers, provider.GetRequiredService<ICacheService>()) { SecretKey = NormalizeKey(configuration["SECURITY_ENCRYPTION_TOKEN"]!, 32) }); return services; } }