feat: harden SaaS authentication and authorization

This commit is contained in:
2026-07-28 12:15:51 +08:00
parent f22f329d33
commit 5d2248efee
123 changed files with 9090 additions and 2822 deletions

View File

@@ -1,15 +1,13 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Tiku.Application.Auth;
using Tiku.Application.Security;
using Tiku.Domain.Tenancy;
namespace Tiku.Infrastructure.Auth;
public sealed class TokenService(IOptions<JwtOptions> options) : ITokenService
public sealed class TokenService(IOptions<JwtOptions> options, IJwtKeyRing keyRing) : ITokenService
{
private readonly JwtOptions options = options.Value;
@@ -18,17 +16,30 @@ public sealed class TokenService(IOptions<JwtOptions> options) : ITokenService
Guid sessionId,
string? phone,
string? email,
TenantMembership membership)
AuthRealm realm,
Guid? tenantId,
bool mfaSatisfied)
{
var expiresAt = DateTimeOffset.UtcNow.AddMinutes(options.AccessTokenMinutes);
var claims = new List<Claim>
{
new(TikuClaimTypes.UserId, userId.ToString()),
new(JwtRegisteredClaimNames.Sub, userId.ToString()),
new(TikuClaimTypes.SessionId, sessionId.ToString()),
new(TikuClaimTypes.TenantId, membership.TenantId.ToString()),
new(TikuClaimTypes.TenantRole, membership.Role.ToString())
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")),
new(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new(TikuClaimTypes.Realm, realm.ToString().ToLowerInvariant())
};
if (tenantId.HasValue)
{
claims.Add(new Claim(TikuClaimTypes.TenantId, tenantId.Value.ToString()));
}
if (mfaSatisfied)
{
claims.Add(new Claim(TikuClaimTypes.Mfa, "mfa"));
}
if (!string.IsNullOrWhiteSpace(phone))
{
claims.Add(new Claim(TikuClaimTypes.Phone, phone));
@@ -39,15 +50,12 @@ public sealed class TokenService(IOptions<JwtOptions> options) : ITokenService
claims.Add(new Claim(TikuClaimTypes.Email, email));
}
var credentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SigningKey)),
SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
options.Issuer,
options.Audience,
claims,
expires: expiresAt.UtcDateTime,
signingCredentials: credentials);
signingCredentials: keyRing.SigningCredentials);
return (new JwtSecurityTokenHandler().WriteToken(token), expiresAt);
}