97 lines
3.7 KiB
C#
97 lines
3.7 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Service for generating JWT tokens for user authentication.
|
|
/// </summary>
|
|
public interface IJwtTokenService
|
|
{
|
|
/// <summary>
|
|
/// Generates a JWT token for the specified user.
|
|
/// </summary>
|
|
/// <param name="user">The user entity.</param>
|
|
/// <returns>A tuple containing the generated token string and its expiration date.</returns>
|
|
(string token, DateTime expiresAt) GenerateToken(UserEntity user);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public class JwtTokenService : IJwtTokenService
|
|
{
|
|
private readonly string _secretKey;
|
|
private readonly string _issuer;
|
|
private readonly string _audience;
|
|
private readonly int _expiryDays;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="JwtTokenService"/> class.
|
|
/// </summary>
|
|
/// <param name="configuration">The application configuration.</param>
|
|
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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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<Claim>
|
|
{
|
|
// 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);
|
|
}
|
|
} |