[AZ-487] fix: JWT factory + tests now pass on net8.0

- JwtTokenFactory.Create: negative `lifetime` produced Expires < NotBefore
  which `JwtSecurityToken` rejects at construction time. Shift NotBefore
  behind Expires whenever the requested lifetime is non-positive so the
  expired-token fixture round-trips and lifetime validation can fire.
- JwtTokenFactoryTests: validate against a handler with
  `MapInboundClaims = false` so assertions read the factory's own claim
  names ("sub", "email", "permissions") rather than the .NET-default
  remapped ClaimTypes.* aliases.

These were latent — masked by the CS0104 build break fixed in 753be43.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-11 23:45:12 +03:00
parent 753be43d11
commit f64d0d760a
2 changed files with 11 additions and 4 deletions
@@ -14,10 +14,12 @@ public class JwtTokenFactoryTests
[Fact] [Fact]
public void Create_ProducesTokenValidatedByMatchingParameters() public void Create_ProducesTokenValidatedByMatchingParameters()
{ {
// Arrange // Arrange — disable inbound claim remapping so the test asserts
// the factory's actual output ("sub", "email", ...) rather than
// the framework's ClaimTypes.* aliases.
var token = JwtTokenFactory.Create(Secret, subject: "alice"); var token = JwtTokenFactory.Create(Secret, subject: "alice");
var parameters = BuildParameters(Secret); var parameters = BuildParameters(Secret);
var handler = new JwtSecurityTokenHandler(); var handler = new JwtSecurityTokenHandler { MapInboundClaims = false };
// Act // Act
var principal = handler.ValidateToken(token, parameters, out var validatedToken); var principal = handler.ValidateToken(token, parameters, out var validatedToken);
@@ -40,7 +42,7 @@ public class JwtTokenFactoryTests
new Claim("permissions", "FL") new Claim("permissions", "FL")
}; };
var token = JwtTokenFactory.Create(Secret, extraClaims: claims); var token = JwtTokenFactory.Create(Secret, extraClaims: claims);
var handler = new JwtSecurityTokenHandler(); var handler = new JwtSecurityTokenHandler { MapInboundClaims = false };
// Act // Act
var principal = handler.ValidateToken(token, BuildParameters(Secret), out _); var principal = handler.ValidateToken(token, BuildParameters(Secret), out _);
@@ -24,6 +24,11 @@ public static class JwtTokenFactory
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var expires = now.Add(lifetime ?? TimeSpan.FromHours(1)); var expires = now.Add(lifetime ?? TimeSpan.FromHours(1));
// JwtSecurityToken rejects Expires <= NotBefore. For negative
// lifetimes (expired-token test fixture) shift NotBefore behind
// Expires so the constructor accepts the token and lifetime
// validation can fire downstream.
var notBefore = expires <= now ? expires.AddMinutes(-5) : now;
var claims = new List<Claim> var claims = new List<Claim>
{ {
@@ -40,7 +45,7 @@ public static class JwtTokenFactory
issuer: null, issuer: null,
audience: null, audience: null,
claims: claims, claims: claims,
notBefore: now, notBefore: notBefore,
expires: expires, expires: expires,
signingCredentials: credentials); signingCredentials: credentials);