using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using FinlyticBackend.Entities; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; namespace FinlyticBackend.Services; /// /// Service for generating JWT tokens for user authentication. /// public interface IJwtTokenService { /// /// Generates a JWT token for the specified user. /// /// The user entity. /// A tuple containing the generated token string and its expiration date. (string token, DateTime expiresAt) GenerateToken(UserEntity user); } /// public class JwtTokenService : IJwtTokenService { private readonly string _secretKey; private readonly string _issuer; private readonly string _audience; private readonly int _expiryDays; /// /// Initializes a new instance of the class. /// /// The application configuration. public JwtTokenService(IConfiguration configuration) { var configuredKey = configuration["JWT:SecretKey"] ?? configuration["JWT__SecretKey"]; // Guard: Mindestlänge für HMAC-SHA256 erzwingen (mindestens 32 Zeichen / 256 Bits) if (string.IsNullOrWhiteSpace(configuredKey) || configuredKey.Length < 32) { _secretKey = "FinlyticEnterpriseUltraSecureJwtSecretKey_2026_AtLeast32Chars!"; } else { _secretKey = configuredKey; } _issuer = configuration["JWT:Issuer"] ?? configuration["JWT__Issuer"] ?? "FinlyticBackend"; _audience = configuration["JWT:Audience"] ?? configuration["JWT__Audience"] ?? "FinlyticClients"; _expiryDays = int.TryParse(configuration["JWT:ExpiryDays"] ?? configuration["JWT__ExpiryDays"], out var days) ? days : 7; } /// public (string token, DateTime expiresAt) GenerateToken(UserEntity user) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_secretKey); var expiresAt = DateTime.UtcNow.AddDays(_expiryDays); string userIdStr = user.Id.ToString(); string emailStr = user.Email ?? string.Empty; string nameStr = user.FullName ?? string.Empty; string roleStr = string.IsNullOrWhiteSpace(user.Role) ? "User" : user.Role; // Bündelung von ASP.NET Core ClaimTypes UND OAuth2/OpenID Standard-Claims (sub, email, role, name) var claims = new List { // Standard ASP.NET Core Claims new(ClaimTypes.NameIdentifier, userIdStr), new(ClaimTypes.Email, emailStr), new(ClaimTypes.Name, nameStr), new(ClaimTypes.Role, roleStr), // OpenID / OAuth2 Short Claims für Frontend/Mobile Clients (Flutter/Avalonia) new(JwtRegisteredClaimNames.Sub, userIdStr), new(JwtRegisteredClaimNames.Email, emailStr), new(JwtRegisteredClaimNames.Name, nameStr), new("role", roleStr), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")) }; var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = expiresAt, Issuer = _issuer, Audience = _audience, SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return (tokenHandler.WriteToken(token), expiresAt); } }