diff --git a/src/api/SIGCM2.Infrastructure/Security/JwtService.cs b/src/api/SIGCM2.Infrastructure/Security/JwtService.cs
index 02ac8c7..85ab73e 100644
--- a/src/api/SIGCM2.Infrastructure/Security/JwtService.cs
+++ b/src/api/SIGCM2.Infrastructure/Security/JwtService.cs
@@ -1,6 +1,5 @@
using System.Security.Claims;
using System.Security.Cryptography;
-using System.Text.Json;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using SIGCM2.Application.Abstractions.Security;
@@ -44,13 +43,17 @@ public sealed class JwtService : IJwtService
return principal;
}
+ ///
+ /// UDT-009: Generates an access token with minimal claims.
+ /// Claim 'permisos' has been removed — authorization handler resolves permissions
+ /// from DB per-request using IUsuarioRepository + PermisoResolver.
+ /// Token claims: sub, jti, name, rol (+ standard iat/exp/nbf).
+ ///
public string GenerateAccessToken(Usuario usuario)
{
var signingKey = new RsaSecurityKey(_rsa);
var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
- var permisos = DeserializePermisos(usuario.PermisosJson);
-
var claims = new List
{
new(JwtRegisteredClaimNames.Sub, usuario.Id.ToString()),
@@ -59,10 +62,6 @@ public sealed class JwtService : IJwtService
new("rol", usuario.Rol),
};
- // Add each permission as a separate claim
- foreach (var permiso in permisos)
- claims.Add(new Claim("permisos", permiso));
-
var now = DateTime.UtcNow;
var descriptor = new SecurityTokenDescriptor
{
@@ -78,16 +77,4 @@ public sealed class JwtService : IJwtService
var token = handler.CreateToken(descriptor);
return handler.WriteToken(token);
}
-
- private static string[] DeserializePermisos(string permisosJson)
- {
- try
- {
- return JsonSerializer.Deserialize(permisosJson) ?? [];
- }
- catch
- {
- return [];
- }
- }
}
diff --git a/tests/SIGCM2.Application.Tests/Infrastructure/JwtServiceTests.cs b/tests/SIGCM2.Application.Tests/Infrastructure/JwtServiceTests.cs
index cea3266..c080c8f 100644
--- a/tests/SIGCM2.Application.Tests/Infrastructure/JwtServiceTests.cs
+++ b/tests/SIGCM2.Application.Tests/Infrastructure/JwtServiceTests.cs
@@ -55,6 +55,59 @@ public class JwtServiceTests : IDisposable
Assert.Contains("sigcm2.web", parsed.Audiences); // aud
Assert.Contains(parsed.Claims, c => c.Type == "name" && c.Value == "admin");
Assert.Contains(parsed.Claims, c => c.Type == "rol" && c.Value == "admin");
+
+ // J-01 (UDT-009): token must NOT contain 'permisos' claim post-UDT-009
+ Assert.DoesNotContain(parsed.Claims, c => c.Type == "permisos");
+ }
+
+ // J-01: token post-UDT-009 does NOT have 'permisos' claim
+ [Fact]
+ public void GenerateAccessToken_DoesNotContainPermisosClaim()
+ {
+ var usuario = MakeUsuario();
+ var token = _jwtService.GenerateAccessToken(usuario);
+
+ var handler = new JwtSecurityTokenHandler();
+ var parsed = handler.ReadJwtToken(token);
+
+ Assert.DoesNotContain(parsed.Claims, c => c.Type == "permisos");
+ }
+
+ // J-02: claims present are sub, jti, name, rol (+ iat/exp/nbf) — no extras
+ [Fact]
+ public void GenerateAccessToken_HasExactlyExpectedClaims_NoPermisos()
+ {
+ var usuario = MakeUsuario();
+ var token = _jwtService.GenerateAccessToken(usuario);
+
+ var handler = new JwtSecurityTokenHandler();
+ var parsed = handler.ReadJwtToken(token);
+
+ // Must have sub, name, rol, jti
+ Assert.Contains(parsed.Claims, c => c.Type == "sub");
+ Assert.Contains(parsed.Claims, c => c.Type == "name");
+ Assert.Contains(parsed.Claims, c => c.Type == "rol");
+ Assert.Contains(parsed.Claims, c => c.Type == "jti");
+
+ // Must NOT have permisos
+ Assert.DoesNotContain(parsed.Claims, c => c.Type == "permisos");
+ }
+
+ // J-03: MakeUsuario with '["*"]' PermisosJson → token still has no 'permisos' claim
+ [Fact]
+ public void GenerateAccessToken_WithLegacyPermisosJson_NoPermisosClaim()
+ {
+ // MakeUsuario already uses '[\"*\"]' — this explicitly tests J-03
+ var usuario = MakeUsuario();
+ Assert.Equal("[\"*\"]", usuario.PermisosJson); // verify the helper
+
+ var token = _jwtService.GenerateAccessToken(usuario);
+
+ var handler = new JwtSecurityTokenHandler();
+ var parsed = handler.ReadJwtToken(token);
+
+ // Post-UDT-009: JwtService ignores PermisosJson entirely — no claim emitted
+ Assert.DoesNotContain(parsed.Claims, c => c.Type == "permisos");
}
// Scenario: token is verifiable with the public key