feat(Backend): update API gateway and websocket hubs
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user