Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to create token without kid #2968

Merged
merged 1 commit into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, System.Collections.Generic.IDictionary<string, object> 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<string, object> 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<string, object> jweHeaderClaims, System.Collections.Generic.IDictionary<string, object> jwsHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -255,7 +256,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
tokenDescriptor.EncryptingCredentials,
tokenDescriptor.CompressionAlgorithm,
tokenDescriptor.AdditionalHeaderClaims,
tokenDescriptor.TokenType);
tokenDescriptor.TokenType,
true);
}
else
{
Expand Down Expand Up @@ -586,7 +588,8 @@ internal static string CreateToken
encryptingCredentials,
additionalHeaderClaims,
additionalInnerHeaderClaims,
null);
null,
true);

// mark length of jwt header
int headerLength = (int)utf8ByteMemoryStream.Length;
Expand Down Expand Up @@ -638,7 +641,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
encryptingCredentials,
compressionAlgorithm,
additionalHeaderClaims,
tokenType);
tokenType,
true);
}
else
{
Expand Down Expand Up @@ -959,7 +963,8 @@ internal static void WriteJwsHeader(
EncryptingCredentials encryptingCredentials,
IDictionary<string, object> jweHeaderClaims,
IDictionary<string, object> jwsHeaderClaims,
string tokenType)
string tokenType,
bool includeKeyIdInHeader)
{
if (jweHeaderClaims?.Count > 0 && jweHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any())
throw LogHelper.LogExceptionMessage(
Expand Down Expand Up @@ -991,11 +996,14 @@ internal static void WriteJwsHeader(
else
{
writer.WriteString(JwtHeaderUtf8Bytes.Alg, signingCredentials.Algorithm);
if (signingCredentials.Key.KeyId != null)
writer.WriteString(JwtHeaderUtf8Bytes.Kid, signingCredentials.Key.KeyId);
if (includeKeyIdInHeader)
{
if (signingCredentials.Key.KeyId != null)
writer.WriteString(JwtHeaderUtf8Bytes.Kid, signingCredentials.Key.KeyId);

if (signingCredentials.Key is X509SecurityKey x509SecurityKey)
writer.WriteString(JwtHeaderUtf8Bytes.X5t, x509SecurityKey.X5t);
if (signingCredentials.Key is X509SecurityKey x509SecurityKey)
writer.WriteString(JwtHeaderUtf8Bytes.X5t, x509SecurityKey.X5t);
}
}

// Priority is additionalInnerHeaderClaims, additionalHeaderClaims, defaults
Expand Down Expand Up @@ -1033,7 +1041,8 @@ internal static byte[] WriteJweHeader(
EncryptingCredentials encryptingCredentials,
string compressionAlgorithm,
string tokenType,
IDictionary<string, object> jweHeaderClaims)
IDictionary<string, object> jweHeaderClaims,
bool includeKeyIdInHeader)
{
using (MemoryStream memoryStream = new())
{
Expand All @@ -1053,7 +1062,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))
Expand All @@ -1069,7 +1078,7 @@ internal static byte[] WriteJweHeader(
}
else
{
if (encryptingCredentials.Key.KeyId != null)
if (includeKeyIdInHeader && encryptingCredentials.Key.KeyId != null)
writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.Key.KeyId);
}

Expand Down Expand Up @@ -1254,20 +1263,22 @@ private static string EncryptTokenPrivate(
IDictionary<string, object> 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(
byte[] innerTokenUtf8Bytes,
EncryptingCredentials encryptingCredentials,
string compressionAlgorithm,
IDictionary<string, object> additionalHeaderClaims,
string tokenType)
string tokenType,
bool includeKeyIdInHeader)
{
CryptoProviderFactory cryptoProviderFactory = encryptingCredentials.CryptoProviderFactory ?? encryptingCredentials.Key.CryptoProviderFactory;

Expand All @@ -1281,7 +1292,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))
{
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.get -> bool
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.set -> void
11 changes: 11 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Claims;
using System.Threading;

Expand Down Expand Up @@ -105,5 +106,15 @@ public class SecurityTokenDescriptor
/// values will be overridden.
/// </summary>
public ClaimsIdentity Subject { get; set; }

/// <summary>
/// Indicates if <c>kid</c> and <c>x5t</c> should be included in the header of a JSON web token (JWT)
///
/// <remarks>
/// Only applies to JWTs
/// </remarks>
/// </summary>
[DefaultValue(true)]
public bool IncludeKeyIdInHeader { get; set; } = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
System.IdentityModel.Tokens.Jwt.JwtHeader.JwtHeader(Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary<string, string> outboundAlgorithmMap, string tokenType, System.Collections.Generic.IDictionary<string, object> additionalHeaderClaims, bool includeKeyIdInHeader) -> void
System.IdentityModel.Tokens.Jwt.JwtHeader.JwtHeader(Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, System.Collections.Generic.IDictionary<string, string> outboundAlgorithmMap, string tokenType, System.Collections.Generic.IDictionary<string, object> additionalInnerHeaderClaims, bool includeKeyIdInHeader) -> void
66 changes: 58 additions & 8 deletions src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> will be added as the value for the 'typ' claim in the header. If it is null or empty <see cref="JwtConstants.HeaderType"/> will be used as token type</param>
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, string> outboundAlgorithmMap, string tokenType, IDictionary<string, object> additionalInnerHeaderClaims)
/// <param name="includeKeyIdInHeader">Controls if key identifying information should be stored in the header</param>
internal JwtHeader(
SigningCredentials signingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalInnerHeaderClaims,
bool includeKeyIdInHeader)
: base(StringComparer.Ordinal)
{
if (signingCredentials == null)
Expand All @@ -144,11 +150,14 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
else
Alg = signingCredentials.Algorithm;

if (!string.IsNullOrEmpty(signingCredentials.Key.KeyId))
Kid = signingCredentials.Key.KeyId;
if (includeKeyIdInHeader)
{
if (!string.IsNullOrEmpty(signingCredentials.Key.KeyId))
Kid = signingCredentials.Key.KeyId;

if (signingCredentials is X509SigningCredentials x509SigningCredentials)
this[JwtHeaderParameterNames.X5t] = Base64UrlEncoder.Encode(x509SigningCredentials.Certificate.GetCertHash());
if (signingCredentials is X509SigningCredentials x509SigningCredentials)
this[JwtHeaderParameterNames.X5t] = Base64UrlEncoder.Encode(x509SigningCredentials.Certificate.GetCertHash());
}
}

if (string.IsNullOrEmpty(tokenType))
Expand All @@ -160,6 +169,23 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
SigningCredentials = signingCredentials;
}

/// <summary>
/// Initializes a new instance of <see cref="JwtHeader"/>.
/// With the Header Parameters:
/// <para>{ { typ, JWT }, { alg, SigningCredentials.Algorithm } }</para>
/// </summary>
/// <param name="signingCredentials"><see cref="SigningCredentials"/> used when creating a JWS Compact JSON.</param>
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> will be added as the value for the 'typ' claim in the header. If it is null or empty <see cref="JwtConstants.HeaderType"/> will be used as token type</param>
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
public JwtHeader(
SigningCredentials signingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalInnerHeaderClaims)
: this(signingCredentials, outboundAlgorithmMap, tokenType, additionalInnerHeaderClaims, true)
{ }

/// <summary>
/// Initializes a new instance of <see cref="JwtHeader"/>.
/// With the Header Parameters:
Expand Down Expand Up @@ -196,8 +222,14 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> provides the token type</param>
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
/// <param name="includeKeyIdInHeader">Controls if key identifying information should be stored in the header</param>
/// <exception cref="ArgumentNullException">If 'encryptingCredentials' is null.</exception>
public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string, string> outboundAlgorithmMap, string tokenType, IDictionary<string, object> additionalHeaderClaims)
internal JwtHeader(
EncryptingCredentials encryptingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalHeaderClaims,
bool includeKeyIdInHeader)
: base(StringComparer.Ordinal)
{
if (encryptingCredentials == null)
Expand All @@ -221,7 +253,7 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
// needs to be maintained.
if (AppContextSwitches.UseRfcDefinitionOfEpkAndKid)
{
if (!string.IsNullOrEmpty(encryptingCredentials.KeyExchangePublicKey.KeyId))
if (includeKeyIdInHeader && !string.IsNullOrEmpty(encryptingCredentials.KeyExchangePublicKey.KeyId))
Kid = encryptingCredentials.KeyExchangePublicKey.KeyId;

// Parameter MUST be present [...] when [key agreement] algorithms are used: https://www.rfc-editor.org/rfc/rfc7518#section-4.6.1.1
Expand All @@ -230,7 +262,7 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
}
else
{
if (!string.IsNullOrEmpty(encryptingCredentials.Key.KeyId))
if (includeKeyIdInHeader && !string.IsNullOrEmpty(encryptingCredentials.Key.KeyId))
Kid = encryptingCredentials.Key.KeyId;
}

Expand All @@ -243,6 +275,24 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
EncryptingCredentials = encryptingCredentials;
}

/// <summary>
/// Initializes a new instance of <see cref="JwtHeader"/>.
/// With the Header Parameters:
/// <para>{ { typ, JWT }, { alg, EncryptingCredentials.Algorithm } }</para>
/// </summary>
/// <param name="encryptingCredentials"><see cref="EncryptingCredentials"/> used when creating a JWS Compact JSON.</param>
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> provides the token type</param>
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
/// <exception cref="ArgumentNullException">If 'encryptingCredentials' is null.</exception>
public JwtHeader(
EncryptingCredentials encryptingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalHeaderClaims)
: this(encryptingCredentials, outboundAlgorithmMap, tokenType, additionalHeaderClaims, true)
{ }

/// <summary>
/// Gets the signature algorithm that was used to create the signature.
/// </summary>
Expand Down
Loading
Loading