diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt b/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt index e69de29bb2..16af108561 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt @@ -0,0 +1,3 @@ +static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, System.Collections.Generic.IDictionary additionalHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> string +static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJweHeader(Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, string tokenType, System.Collections.Generic.IDictionary jweHeaderClaims, bool includeKeyIdInHeader) -> byte[] +static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJwsHeader(ref System.Text.Json.Utf8JsonWriter writer, Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary jweHeaderClaims, System.Collections.Generic.IDictionary jwsHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> void \ No newline at end of file diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs index b42f202bfb..fc88df7a2a 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs @@ -195,7 +195,8 @@ internal static string CreateToken( tokenDescriptor.EncryptingCredentials, tokenDescriptor.AdditionalHeaderClaims, tokenDescriptor.AdditionalInnerHeaderClaims, - tokenDescriptor.TokenType); + tokenDescriptor.TokenType, + tokenDescriptor.IncludeKeyIdInHeader); // mark length of jwt header int headerLength = (int)utf8ByteMemoryStream.Length; @@ -255,7 +256,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes tokenDescriptor.EncryptingCredentials, tokenDescriptor.CompressionAlgorithm, tokenDescriptor.AdditionalHeaderClaims, - tokenDescriptor.TokenType); + tokenDescriptor.TokenType, + true); } else { @@ -586,7 +588,8 @@ internal static string CreateToken encryptingCredentials, additionalHeaderClaims, additionalInnerHeaderClaims, - null); + null, + true); // mark length of jwt header int headerLength = (int)utf8ByteMemoryStream.Length; @@ -638,7 +641,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes encryptingCredentials, compressionAlgorithm, additionalHeaderClaims, - tokenType); + tokenType, + true); } else { @@ -959,7 +963,8 @@ internal static void WriteJwsHeader( EncryptingCredentials encryptingCredentials, IDictionary jweHeaderClaims, IDictionary jwsHeaderClaims, - string tokenType) + string tokenType, + bool includeKeyIdInHeader) { if (jweHeaderClaims?.Count > 0 && jweHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any()) throw LogHelper.LogExceptionMessage( @@ -991,7 +996,7 @@ internal static void WriteJwsHeader( else { writer.WriteString(JwtHeaderUtf8Bytes.Alg, signingCredentials.Algorithm); - if (signingCredentials.Key.KeyId != null) + if (includeKeyIdInHeader && signingCredentials.Key.KeyId != null) writer.WriteString(JwtHeaderUtf8Bytes.Kid, signingCredentials.Key.KeyId); if (signingCredentials.Key is X509SecurityKey x509SecurityKey) @@ -1033,7 +1038,8 @@ internal static byte[] WriteJweHeader( EncryptingCredentials encryptingCredentials, string compressionAlgorithm, string tokenType, - IDictionary jweHeaderClaims) + IDictionary jweHeaderClaims, + bool includeKeyIdInHeader) { using (MemoryStream memoryStream = new()) { @@ -1053,7 +1059,7 @@ internal static byte[] WriteJweHeader( // needs to be maintained. if (AppContextSwitches.UseRfcDefinitionOfEpkAndKid) { - if (encryptingCredentials.KeyExchangePublicKey.KeyId != null) + if (includeKeyIdInHeader && encryptingCredentials.KeyExchangePublicKey.KeyId != null) writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.KeyExchangePublicKey.KeyId); if (SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(encryptingCredentials.Alg)) @@ -1069,7 +1075,7 @@ internal static byte[] WriteJweHeader( } else { - if (encryptingCredentials.Key.KeyId != null) + if (includeKeyIdInHeader && encryptingCredentials.Key.KeyId != null) writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.Key.KeyId); } @@ -1254,12 +1260,13 @@ private static string EncryptTokenPrivate( IDictionary additionalHeaderClaims, string tokenType) { - return (EncryptToken( - Encoding.UTF8.GetBytes(innerJwt), - encryptingCredentials, - compressionAlgorithm, - additionalHeaderClaims, - tokenType)); + return EncryptToken( + Encoding.UTF8.GetBytes(innerJwt), + encryptingCredentials, + compressionAlgorithm, + additionalHeaderClaims, + tokenType, + true); } internal static string EncryptToken( @@ -1267,7 +1274,8 @@ internal static string EncryptToken( EncryptingCredentials encryptingCredentials, string compressionAlgorithm, IDictionary additionalHeaderClaims, - string tokenType) + string tokenType, + bool includeKeyIdInHeader) { CryptoProviderFactory cryptoProviderFactory = encryptingCredentials.CryptoProviderFactory ?? encryptingCredentials.Key.CryptoProviderFactory; @@ -1281,7 +1289,7 @@ internal static string EncryptToken( if (encryptionProvider == null) throw LogHelper.LogExceptionMessage(new SecurityTokenEncryptionFailedException(LogMessages.IDX14103)); - byte[] jweHeader = WriteJweHeader(encryptingCredentials, compressionAlgorithm, tokenType, additionalHeaderClaims); + byte[] jweHeader = WriteJweHeader(encryptingCredentials, compressionAlgorithm, tokenType, additionalHeaderClaims, includeKeyIdInHeader); byte[] plainText; if (!string.IsNullOrEmpty(compressionAlgorithm)) { diff --git a/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt index e69de29bb2..d2c20a77d4 100644 --- a/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt +++ b/src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt @@ -0,0 +1,2 @@ +Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.get -> bool +Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.set -> void diff --git a/src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs b/src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs index b9b5843143..3b327a48b4 100644 --- a/src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs +++ b/src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Security.Claims; using System.Threading; @@ -105,5 +106,15 @@ public class SecurityTokenDescriptor /// values will be overridden. /// public ClaimsIdentity Subject { get; set; } + + /// + /// Indicates if kid and x5t should be included in the header of a JSON web token (JWT) + /// + /// + /// Only applies to JWTs + /// + /// + [DefaultValue(true)] + public bool IncludeKeyIdInHeader { get; set; } = true; } } diff --git a/src/System.IdentityModel.Tokens.Jwt/InternalAPI.Unshipped.txt b/src/System.IdentityModel.Tokens.Jwt/InternalAPI.Unshipped.txt index e69de29bb2..47d5d10352 100644 --- a/src/System.IdentityModel.Tokens.Jwt/InternalAPI.Unshipped.txt +++ b/src/System.IdentityModel.Tokens.Jwt/InternalAPI.Unshipped.txt @@ -0,0 +1,2 @@ +System.IdentityModel.Tokens.Jwt.JwtHeader.JwtHeader(Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary outboundAlgorithmMap, string tokenType, System.Collections.Generic.IDictionary additionalHeaderClaims, bool includeKeyIdInHeader) -> void +System.IdentityModel.Tokens.Jwt.JwtHeader.JwtHeader(Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, System.Collections.Generic.IDictionary outboundAlgorithmMap, string tokenType, System.Collections.Generic.IDictionary additionalInnerHeaderClaims, bool includeKeyIdInHeader) -> void \ No newline at end of file diff --git a/src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs b/src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs index 4e04e5da3d..333567edad 100644 --- a/src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs +++ b/src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs @@ -131,7 +131,13 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionaryprovides a mapping for the 'alg' value so that values are within the JWT namespace. /// will be added as the value for the 'typ' claim in the header. If it is null or empty will be used as token type /// Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header. - public JwtHeader(SigningCredentials signingCredentials, IDictionary outboundAlgorithmMap, string tokenType, IDictionary additionalInnerHeaderClaims) + /// Controls if key identifying information should be stored in the header + internal JwtHeader( + SigningCredentials signingCredentials, + IDictionary outboundAlgorithmMap, + string tokenType, + IDictionary additionalInnerHeaderClaims, + bool includeKeyIdInHeader) : base(StringComparer.Ordinal) { if (signingCredentials == null) @@ -144,7 +150,7 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary + /// Initializes a new instance of . + /// With the Header Parameters: + /// { { typ, JWT }, { alg, SigningCredentials.Algorithm } } + /// + /// used when creating a JWS Compact JSON. + /// provides a mapping for the 'alg' value so that values are within the JWT namespace. + /// will be added as the value for the 'typ' claim in the header. If it is null or empty will be used as token type + /// Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header. + public JwtHeader( + SigningCredentials signingCredentials, + IDictionary outboundAlgorithmMap, + string tokenType, + IDictionary additionalInnerHeaderClaims) + : this(signingCredentials, outboundAlgorithmMap, tokenType, additionalInnerHeaderClaims, true) + { } + /// /// Initializes a new instance of . /// With the Header Parameters: @@ -196,8 +219,14 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionaryprovides a mapping for the 'alg' value so that values are within the JWT namespace. /// provides the token type /// Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header. + /// Controls if key identifying information should be stored in the header /// If 'encryptingCredentials' is null. - public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary outboundAlgorithmMap, string tokenType, IDictionary additionalHeaderClaims) + internal JwtHeader( + EncryptingCredentials encryptingCredentials, + IDictionary outboundAlgorithmMap, + string tokenType, + IDictionary additionalHeaderClaims, + bool includeKeyIdInHeader) : base(StringComparer.Ordinal) { if (encryptingCredentials == null) @@ -221,7 +250,7 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary + /// Initializes a new instance of . + /// With the Header Parameters: + /// { { typ, JWT }, { alg, EncryptingCredentials.Algorithm } } + /// + /// used when creating a JWS Compact JSON. + /// provides a mapping for the 'alg' value so that values are within the JWT namespace. + /// provides the token type + /// Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header. + /// If 'encryptingCredentials' is null. + public JwtHeader( + EncryptingCredentials encryptingCredentials, + IDictionary outboundAlgorithmMap, + string tokenType, + IDictionary additionalHeaderClaims) + : this(encryptingCredentials, outboundAlgorithmMap, tokenType, additionalHeaderClaims, true) + { } + /// /// Gets the signature algorithm that was used to create the signature. /// diff --git a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs index bad33a8ed9..ed16a28516 100644 --- a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs +++ b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs @@ -356,7 +356,7 @@ public virtual string CreateEncodedJwt( expires, issuedAt, signingCredentials, - null, null, null, null, null).RawData; + null, null, null, null, null, true).RawData; } /// @@ -395,7 +395,7 @@ public virtual string CreateEncodedJwt( expires, issuedAt, signingCredentials, - encryptingCredentials, null, null, null, null).RawData; + encryptingCredentials, null, null, null, null, true).RawData; } /// @@ -437,7 +437,7 @@ public virtual string CreateEncodedJwt( issuedAt, signingCredentials, encryptingCredentials, - claimCollection, null, null, null).RawData; + claimCollection, null, null, null, true).RawData; } /// @@ -463,7 +463,8 @@ public virtual JwtSecurityToken CreateJwtSecurityToken(SecurityTokenDescriptor t tokenDescriptor.Claims, tokenDescriptor.TokenType, tokenDescriptor.AdditionalHeaderClaims, - tokenDescriptor.AdditionalInnerHeaderClaims); + tokenDescriptor.AdditionalInnerHeaderClaims, + tokenDescriptor.IncludeKeyIdInHeader); } /// @@ -505,7 +506,7 @@ public virtual JwtSecurityToken CreateJwtSecurityToken( expires, issuedAt, signingCredentials, - encryptingCredentials, null, null, null, null); + encryptingCredentials, null, null, null, null, true); } /// @@ -550,7 +551,7 @@ public virtual JwtSecurityToken CreateJwtSecurityToken( issuedAt, signingCredentials, encryptingCredentials, - claimCollection, null, null, null); + claimCollection, null, null, null, true); } /// @@ -588,7 +589,7 @@ public virtual JwtSecurityToken CreateJwtSecurityToken( notBefore, expires, issuedAt, - signingCredentials, null, null, null, null, null); + signingCredentials, null, null, null, null, null, true); } /// @@ -614,7 +615,8 @@ public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescripto tokenDescriptor.Claims, tokenDescriptor.TokenType, tokenDescriptor.AdditionalHeaderClaims, - tokenDescriptor.AdditionalInnerHeaderClaims); + tokenDescriptor.AdditionalInnerHeaderClaims, + tokenDescriptor.IncludeKeyIdInHeader); } private JwtSecurityToken CreateJwtSecurityTokenPrivate( @@ -629,11 +631,12 @@ private JwtSecurityToken CreateJwtSecurityTokenPrivate( IDictionary claimCollection, string tokenType, IDictionary additionalHeaderClaims, - IDictionary additionalInnerHeaderClaims) + IDictionary additionalInnerHeaderClaims, + bool includeKidInHeader) { return CreateJwtSecurityTokenPrivate( issuer, audience, [], subject, notBefore, expires, issuedAt, signingCredentials, encryptingCredentials, - claimCollection, tokenType, additionalHeaderClaims, additionalInnerHeaderClaims); + claimCollection, tokenType, additionalHeaderClaims, additionalInnerHeaderClaims, includeKidInHeader); } private JwtSecurityToken CreateJwtSecurityTokenPrivate( @@ -649,7 +652,8 @@ private JwtSecurityToken CreateJwtSecurityTokenPrivate( IDictionary claimCollection, string tokenType, IDictionary additionalHeaderClaims, - IDictionary additionalInnerHeaderClaims) + IDictionary additionalInnerHeaderClaims, + bool includeKeyIdInHeader) { if (SetDefaultTimesOnTokenCreation && (!expires.HasValue || !issuedAt.HasValue || !notBefore.HasValue)) { @@ -665,7 +669,7 @@ private JwtSecurityToken CreateJwtSecurityTokenPrivate( } JwtPayload payload = new JwtPayload(issuer, audience, audiences, (subject == null ? null : OutboundClaimTypeTransform(subject.Claims)), (claimCollection == null ? null : OutboundClaimTypeTransform(claimCollection)), notBefore, expires, issuedAt); - JwtHeader header = new JwtHeader(signingCredentials, OutboundAlgorithmMap, tokenType, additionalInnerHeaderClaims); + JwtHeader header = new JwtHeader(signingCredentials, OutboundAlgorithmMap, tokenType, additionalInnerHeaderClaims, includeKeyIdInHeader); if (LogHelper.IsEnabled(EventLogLevel.Verbose)) LogHelper.LogVerbose(LogMessages.IDX12721, LogHelper.MarkAsNonPII(issuer ?? "null"), LogHelper.MarkAsNonPII(payload.Aud.ToString() ?? "null"));