using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using AutoFixture; using Govor.Application.Interfaces.Authentication; using Govor.Application.Services.Authentication; using Govor.Core.Models.Users; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Moq; namespace Govor.Application.Tests.Services.Authentication; [TestFixture] public class JwtServiceTests { private Fixture _fixture; private Mock> _jwtOptionsMock; private Mock> _jwtRefreshOptionsMock; private Mock _invitesServiceMock; private IJwtService _jwtService; private JwtAccessOption _testJwtAccessOptions; private JwtRefreshOption _testJwtRefreshOptions; [SetUp] public void SetUp() { _fixture = new Fixture(); _fixture.Behaviors.OfType().ToList().ForEach(b => _fixture.Behaviors.Remove(b)); _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); _testJwtAccessOptions = new JwtAccessOption { SecretKey = "THIS_IS_A_TEST_SECRET_KEY_THAT_IS_LONG_ENOUGH_1234", // Ensure key size is sufficient for HMACSHA256 Minutes = 5 }; _testJwtRefreshOptions = new JwtRefreshOption() { RefreshTokenLifetimeDays = 30 }; _jwtOptionsMock = new Mock>(); _jwtOptionsMock.Setup(o => o.Value).Returns(_testJwtAccessOptions); _jwtRefreshOptionsMock = new Mock>(); _jwtRefreshOptionsMock.Setup(o => o.Value).Returns(_testJwtRefreshOptions); _invitesServiceMock = new Mock(); _jwtService = new JwtService( _jwtOptionsMock.Object, _jwtRefreshOptionsMock.Object, _invitesServiceMock.Object); } [Test] public async Task GenerateJwtToken_ShouldReturnValidJwtString_WithCorrectClaims() { // Arrange var user = _fixture.Create(); var sessionId = Guid.NewGuid(); var expectedRole = "User"; _invitesServiceMock.Setup(s => s.GetRoleAsync(user)).ReturnsAsync(expectedRole); // Act var tokenString = await _jwtService.GenerateAccessTokenAsync(user, sessionId); // Assert var handler = new JwtSecurityTokenHandler(); var jwt = handler.ReadJwtToken(tokenString); var claims = jwt.Claims.ToDictionary(c => c.Type, c => c.Value); Assert.That(claims["userId"], Is.EqualTo(user.Id.ToString())); Assert.That(claims["sid"], Is.EqualTo(sessionId.ToString())); Assert.That(claims[ClaimTypes.Role], Is.EqualTo(expectedRole)); Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow)); } [Test] public async Task GenerateAccessTokenAsync_ShouldIncludeSessionIdAndRole() { // Arrange var user = new User { Id = Guid.NewGuid(), Username = "TestUser" }; var sessionId = Guid.NewGuid(); var role = "Admin"; _invitesServiceMock.Setup(s => s.GetRoleAsync(user)).ReturnsAsync(role); // Act var token = await _jwtService.GenerateAccessTokenAsync(user, sessionId); var handler = new JwtSecurityTokenHandler(); var jwt = handler.ReadJwtToken(token); // Assert var sidClaim = jwt.Claims.FirstOrDefault(c => c.Type == "sid"); var roleClaim = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role); Assert.That(sidClaim?.Value, Is.EqualTo(sessionId.ToString())); Assert.That(roleClaim?.Value, Is.EqualTo(role)); } [Test] public async Task GenerateRefreshTokenAsync_ReturnsValidRefreshToken() { // Arrange var user = new User { Id = Guid.NewGuid() }; // Act var token = await _jwtService.GenerateRefreshTokenAsync(user); // Assert Assert.That(token, Is.Not.Null); var handler = new JwtSecurityTokenHandler(); var jwt = handler.ReadJwtToken(token); Assert.That(user.Id.ToString(), Is.EqualTo(jwt.Claims.First(c => c.Type == "userId").Value)); Assert.That("refresh",Is.EqualTo(jwt.Claims.First(c => c.Type == "tokenType").Value)); } [Test] public async Task GetPrincipalFromExpiredToken_ReturnsValidClaimsPrincipal() { // Arrange var userId = Guid.NewGuid(); var handler = new JwtSecurityTokenHandler(); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_testJwtAccessOptions.SecretKey)); var now = DateTime.UtcNow; var token = handler.CreateToken(new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("userId", userId.ToString()) }), NotBefore = now.AddSeconds(-10), IssuedAt = now.AddSeconds(-10), Expires = now.AddSeconds(-5), SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256) }); var expiredToken = handler.WriteToken(token); // Act var principal = _jwtService.GetPrincipalFromExpiredToken(expiredToken); // Assert Assert.That(principal, Is.Not.Null); var claim = principal.FindFirst("userId"); Assert.That(claim, Is.Not.Null); Assert.That(userId.ToString(), Is.EqualTo(claim.Value)); } }