using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using JobTrackerApi.Services; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; using Moq; using Xunit; namespace JobTrackerApi.Tests; public sealed class MicrosoftTokenValidatorTests { private static (IConfiguration Config, Mock> ConfigManager, SymmetricSecurityKey Key) BuildHarness() { var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["Auth:MicrosoftClientId"] = "client-123" }) .Build(); var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super-secret-signing-key-super-secret")); var oidc = new OpenIdConnectConfiguration(); oidc.SigningKeys.Add(signingKey); var configManager = new Mock>(); configManager.Setup(x => x.GetConfigurationAsync(It.IsAny())).ReturnsAsync(oidc); return (config, configManager, signingKey); } [Fact] public async Task ValidateAsync_accepts_tenant_scoped_issuer_and_maps_oid_to_subject() { var (config, configManager, signingKey) = BuildHarness(); var token = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken( issuer: "https://login.microsoftonline.com/9f2c1e3a-tenant/v2.0", audience: "client-123", claims: new[] { new Claim("oid", "ms-subject-1"), new Claim("email", "demo@example.com"), new Claim("given_name", "Demo"), new Claim("family_name", "User"), new Claim("name", "Demo User"), }, expires: DateTime.UtcNow.AddMinutes(10), signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256))); var validator = new MicrosoftTokenValidator(config, configManager.Object); var result = await validator.ValidateAsync(token); Assert.Equal("ms-subject-1", result.Subject); Assert.Equal("demo@example.com", result.Email); Assert.True(result.EmailVerified); Assert.Equal("Demo", result.GivenName); Assert.Equal("User", result.FamilyName); } [Fact] public async Task ValidateAsync_rejects_non_microsoft_issuer() { var (config, configManager, signingKey) = BuildHarness(); var token = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken( issuer: "https://evil.example.com/v2.0", audience: "client-123", claims: new[] { new Claim("oid", "ms-subject-1") }, expires: DateTime.UtcNow.AddMinutes(10), signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256))); var validator = new MicrosoftTokenValidator(config, configManager.Object); await Assert.ThrowsAsync(() => validator.ValidateAsync(token)); } }