IdentityHelpersTests.cs
3,851 bytes
| 1 | using System.IdentityModel.Tokens.Jwt; |
|---|---|
| 2 | using System.Security.Claims; |
| 3 | using SplitApp.Shared.Kernel.Auth; |
| 4 | |
| 5 | namespace SplitApp.Modules.Users.Tests; |
| 6 | |
| 7 | public class IdentityHelpersTests |
| 8 | { |
| 9 | private const string Key = "this-is-a-long-enough-test-signing-key-for-hs256"; |
| 10 | private const string Issuer = "splitapp-test"; |
| 11 | private const string Audience = "splitapp-test-audience"; |
| 12 | |
| 13 | [Fact] |
| 14 | public void GenerateJwt_ProducesTokenContainingAllClaims() |
| 15 | { |
| 16 | var claims = new[] |
| 17 | { |
| 18 | new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()), |
| 19 | new Claim(ClaimTypes.Email, "alice@example.com"), |
| 20 | }; |
| 21 | |
| 22 | var jwt = IdentityHelpers.GenerateJwt(claims, Key, Issuer, Audience, expiresInSeconds: 60); |
| 23 | |
| 24 | Assert.False(string.IsNullOrWhiteSpace(jwt)); |
| 25 | |
| 26 | var parsed = new JwtSecurityTokenHandler().ReadJwtToken(jwt); |
| 27 | Assert.Equal(Issuer, parsed.Issuer); |
| 28 | Assert.Contains(parsed.Audiences, a => a == Audience); |
| 29 | Assert.Contains(parsed.Claims, c => c.Type == ClaimTypes.Email && c.Value == "alice@example.com"); |
| 30 | } |
| 31 | |
| 32 | [Fact] |
| 33 | public void ValidateJWT_ReturnsTrue_ForTokenSignedWithSameKey() |
| 34 | { |
| 35 | var jwt = IdentityHelpers.GenerateJwt( |
| 36 | new[] { new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()) }, |
| 37 | Key, Issuer, Audience, expiresInSeconds: 60); |
| 38 | |
| 39 | Assert.True(IdentityHelpers.ValidateJWT(jwt, Key, Issuer, Audience)); |
| 40 | } |
| 41 | |
| 42 | [Fact] |
| 43 | public void ValidateJWT_ReturnsFalse_ForWrongSigningKey() |
| 44 | { |
| 45 | var jwt = IdentityHelpers.GenerateJwt( |
| 46 | new[] { new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()) }, |
| 47 | Key, Issuer, Audience, expiresInSeconds: 60); |
| 48 | |
| 49 | Assert.False(IdentityHelpers.ValidateJWT(jwt, "different-signing-key-different-from-the-original-one", Issuer, Audience)); |
| 50 | } |
| 51 | |
| 52 | [Fact] |
| 53 | public void ValidateJWT_IgnoresExpiration_ForRefreshScenario() |
| 54 | { |
| 55 | var jwt = IdentityHelpers.GenerateJwt( |
| 56 | new[] { new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()) }, |
| 57 | Key, Issuer, Audience, expiresInSeconds: -60); |
| 58 | |
| 59 | Assert.True(IdentityHelpers.ValidateJWT(jwt, Key, Issuer, Audience)); |
| 60 | } |
| 61 | |
| 62 | [Fact] |
| 63 | public void ValidateJWT_ReturnsFalse_ForWrongIssuer() |
| 64 | { |
| 65 | var jwt = IdentityHelpers.GenerateJwt( |
| 66 | new[] { new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()) }, |
| 67 | Key, Issuer, Audience, expiresInSeconds: 60); |
| 68 | |
| 69 | Assert.False(IdentityHelpers.ValidateJWT(jwt, Key, "different-issuer", Audience)); |
| 70 | } |
| 71 | |
| 72 | [Fact] |
| 73 | public void ValidateJWT_ReturnsFalse_ForWrongAudience() |
| 74 | { |
| 75 | var jwt = IdentityHelpers.GenerateJwt( |
| 76 | new[] { new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()) }, |
| 77 | Key, Issuer, Audience, expiresInSeconds: 60); |
| 78 | |
| 79 | Assert.False(IdentityHelpers.ValidateJWT(jwt, Key, Issuer, "different-audience")); |
| 80 | } |
| 81 | |
| 82 | [Fact] |
| 83 | public void ValidateJWT_ReturnsFalse_ForMalformedToken() |
| 84 | { |
| 85 | Assert.False(IdentityHelpers.ValidateJWT("not-a-real-jwt-string", Key, Issuer, Audience)); |
| 86 | Assert.False(IdentityHelpers.ValidateJWT("a.b.c", Key, Issuer, Audience)); |
| 87 | } |
| 88 | |
| 89 | [Fact] |
| 90 | public void GenerateJwt_PreservesCustomClaimType_InProducedToken() |
| 91 | { |
| 92 | var claims = new[] |
| 93 | { |
| 94 | new Claim(ClaimTypes.NameIdentifier, "user-1"), |
| 95 | new Claim("trip_count", "3"), |
| 96 | new Claim(ClaimTypes.Role, "admin"), |
| 97 | }; |
| 98 | |
| 99 | var jwt = IdentityHelpers.GenerateJwt(claims, Key, Issuer, Audience, expiresInSeconds: 60); |
| 100 | var parsed = new JwtSecurityTokenHandler().ReadJwtToken(jwt); |
| 101 | |
| 102 | Assert.Contains(parsed.Claims, c => c.Type == "trip_count" && c.Value == "3"); |
| 103 | Assert.Contains(parsed.Claims, c => c.Value == "admin"); |
| 104 | } |
| 105 | } |
| 106 | |