From 305f0f98f62193ce5189d420d368c688b0bb87e4 Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Mon, 9 Dec 2024 10:11:13 -0600 Subject: [PATCH] Filter protocol claims from reference tokens --- .../Validation/Default/TokenValidator.cs | 7 +- .../IntrospectionRequestValidatorTests.cs | 74 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/IdentityServer/Validation/Default/TokenValidator.cs b/src/IdentityServer/Validation/Default/TokenValidator.cs index 5515a89cc..be1a13903 100644 --- a/src/IdentityServer/Validation/Default/TokenValidator.cs +++ b/src/IdentityServer/Validation/Default/TokenValidator.cs @@ -435,7 +435,12 @@ private IEnumerable ReferenceTokenToClaims(Token token) claims.Add(new Claim(JwtClaimTypes.Audience, aud)); } - claims.AddRange(token.Claims); + claims.AddRange(token.Claims.Where(c => + c.Type != JwtClaimTypes.IssuedAt && + c.Type != JwtClaimTypes.Issuer && + c.Type != JwtClaimTypes.NotBefore && + c.Type != JwtClaimTypes.Expiration + )); return claims; } diff --git a/test/IdentityServer.UnitTests/Validation/IntrospectionRequestValidatorTests.cs b/test/IdentityServer.UnitTests/Validation/IntrospectionRequestValidatorTests.cs index 2b33805d5..e8a9040a7 100644 --- a/test/IdentityServer.UnitTests/Validation/IntrospectionRequestValidatorTests.cs +++ b/test/IdentityServer.UnitTests/Validation/IntrospectionRequestValidatorTests.cs @@ -3,9 +3,11 @@ using System; +using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; +using Duende.IdentityModel; using Duende.IdentityServer.Models; using Duende.IdentityServer.Stores; using Duende.IdentityServer.Validation; @@ -115,4 +117,76 @@ public async Task invalid_token_should_return_inactive() result.Claims.Should().BeNull(); result.Token.Should().Be("invalid"); } + + [Theory] + [MemberData(nameof(DuplicateClaimTestCases))] + [Trait("Category", Category)] + public async Task protocol_claims_should_not_be_duplicated( + string claimType, + System.Security.Claims.Claim duplicateClaim, + Func expectedValueSelector) + { + var token = new Token + { + CreationTime = DateTime.UtcNow, + Issuer = "http://op", + ClientId = "codeclient", + Lifetime = 1000, + Claims = + { + duplicateClaim + } + }; + + var handle = await _referenceTokenStore.StoreReferenceTokenAsync(token); + var param = new NameValueCollection + { + { "token", handle } + }; + + var result = await _subject.ValidateAsync( + new IntrospectionRequestValidationContext + { + Parameters = param, + Api = new ApiResource("api") + } + ); + + result.Claims.Where(c => c.Type == claimType) + .Should() + .HaveCount(1) + .And + .Contain(c => c.Value == expectedValueSelector(token)); + } + + public static IEnumerable DuplicateClaimTestCases() + { + yield return new object[] + { + JwtClaimTypes.IssuedAt, + new System.Security.Claims.Claim(JwtClaimTypes.IssuedAt, "1234"), + (Func)(token => new DateTimeOffset(token.CreationTime).ToUnixTimeSeconds().ToString()) + }; + + yield return new object[] + { + JwtClaimTypes.Issuer, + new System.Security.Claims.Claim(JwtClaimTypes.Issuer, "https://bogus.example.com"), + (Func)(token => token.Issuer) + }; + + yield return new object[] + { + JwtClaimTypes.NotBefore, + new System.Security.Claims.Claim(JwtClaimTypes.NotBefore, "1234"), + (Func)(token => new DateTimeOffset(token.CreationTime).ToUnixTimeSeconds().ToString()) + }; + + yield return new object[] + { + JwtClaimTypes.Expiration, + new System.Security.Claims.Claim(JwtClaimTypes.Expiration, "1234"), + (Func)(token => new DateTimeOffset(token.CreationTime).AddSeconds(token.Lifetime).ToUnixTimeSeconds().ToString()) + }; + } } \ No newline at end of file