Skip to content

Commit a1d55cb

Browse files
jennyf19msbw2
andauthored
Add ability to create token without kid (#2968)
Co-authored-by: Brett White <[email protected]>
1 parent 671e2a2 commit a1d55cb

File tree

8 files changed

+136
-40
lines changed

8 files changed

+136
-40
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
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
2+
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[]
3+
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

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ internal static string CreateToken(
195195
tokenDescriptor.EncryptingCredentials,
196196
tokenDescriptor.AdditionalHeaderClaims,
197197
tokenDescriptor.AdditionalInnerHeaderClaims,
198-
tokenDescriptor.TokenType);
198+
tokenDescriptor.TokenType,
199+
tokenDescriptor.IncludeKeyIdInHeader);
199200

200201
// mark length of jwt header
201202
int headerLength = (int)utf8ByteMemoryStream.Length;
@@ -255,7 +256,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
255256
tokenDescriptor.EncryptingCredentials,
256257
tokenDescriptor.CompressionAlgorithm,
257258
tokenDescriptor.AdditionalHeaderClaims,
258-
tokenDescriptor.TokenType);
259+
tokenDescriptor.TokenType,
260+
true);
259261
}
260262
else
261263
{
@@ -586,7 +588,8 @@ internal static string CreateToken
586588
encryptingCredentials,
587589
additionalHeaderClaims,
588590
additionalInnerHeaderClaims,
589-
null);
591+
null,
592+
true);
590593

591594
// mark length of jwt header
592595
int headerLength = (int)utf8ByteMemoryStream.Length;
@@ -638,7 +641,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
638641
encryptingCredentials,
639642
compressionAlgorithm,
640643
additionalHeaderClaims,
641-
tokenType);
644+
tokenType,
645+
true);
642646
}
643647
else
644648
{
@@ -959,7 +963,8 @@ internal static void WriteJwsHeader(
959963
EncryptingCredentials encryptingCredentials,
960964
IDictionary<string, object> jweHeaderClaims,
961965
IDictionary<string, object> jwsHeaderClaims,
962-
string tokenType)
966+
string tokenType,
967+
bool includeKeyIdInHeader)
963968
{
964969
if (jweHeaderClaims?.Count > 0 && jweHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any())
965970
throw LogHelper.LogExceptionMessage(
@@ -991,11 +996,14 @@ internal static void WriteJwsHeader(
991996
else
992997
{
993998
writer.WriteString(JwtHeaderUtf8Bytes.Alg, signingCredentials.Algorithm);
994-
if (signingCredentials.Key.KeyId != null)
995-
writer.WriteString(JwtHeaderUtf8Bytes.Kid, signingCredentials.Key.KeyId);
999+
if (includeKeyIdInHeader)
1000+
{
1001+
if (signingCredentials.Key.KeyId != null)
1002+
writer.WriteString(JwtHeaderUtf8Bytes.Kid, signingCredentials.Key.KeyId);
9961003

997-
if (signingCredentials.Key is X509SecurityKey x509SecurityKey)
998-
writer.WriteString(JwtHeaderUtf8Bytes.X5t, x509SecurityKey.X5t);
1004+
if (signingCredentials.Key is X509SecurityKey x509SecurityKey)
1005+
writer.WriteString(JwtHeaderUtf8Bytes.X5t, x509SecurityKey.X5t);
1006+
}
9991007
}
10001008

10011009
// Priority is additionalInnerHeaderClaims, additionalHeaderClaims, defaults
@@ -1033,7 +1041,8 @@ internal static byte[] WriteJweHeader(
10331041
EncryptingCredentials encryptingCredentials,
10341042
string compressionAlgorithm,
10351043
string tokenType,
1036-
IDictionary<string, object> jweHeaderClaims)
1044+
IDictionary<string, object> jweHeaderClaims,
1045+
bool includeKeyIdInHeader)
10371046
{
10381047
using (MemoryStream memoryStream = new())
10391048
{
@@ -1053,7 +1062,7 @@ internal static byte[] WriteJweHeader(
10531062
// needs to be maintained.
10541063
if (AppContextSwitches.UseRfcDefinitionOfEpkAndKid)
10551064
{
1056-
if (encryptingCredentials.KeyExchangePublicKey.KeyId != null)
1065+
if (includeKeyIdInHeader && encryptingCredentials.KeyExchangePublicKey.KeyId != null)
10571066
writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.KeyExchangePublicKey.KeyId);
10581067

10591068
if (SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(encryptingCredentials.Alg))
@@ -1069,7 +1078,7 @@ internal static byte[] WriteJweHeader(
10691078
}
10701079
else
10711080
{
1072-
if (encryptingCredentials.Key.KeyId != null)
1081+
if (includeKeyIdInHeader && encryptingCredentials.Key.KeyId != null)
10731082
writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.Key.KeyId);
10741083
}
10751084

@@ -1254,20 +1263,22 @@ private static string EncryptTokenPrivate(
12541263
IDictionary<string, object> additionalHeaderClaims,
12551264
string tokenType)
12561265
{
1257-
return (EncryptToken(
1258-
Encoding.UTF8.GetBytes(innerJwt),
1259-
encryptingCredentials,
1260-
compressionAlgorithm,
1261-
additionalHeaderClaims,
1262-
tokenType));
1266+
return EncryptToken(
1267+
Encoding.UTF8.GetBytes(innerJwt),
1268+
encryptingCredentials,
1269+
compressionAlgorithm,
1270+
additionalHeaderClaims,
1271+
tokenType,
1272+
true);
12631273
}
12641274

12651275
internal static string EncryptToken(
12661276
byte[] innerTokenUtf8Bytes,
12671277
EncryptingCredentials encryptingCredentials,
12681278
string compressionAlgorithm,
12691279
IDictionary<string, object> additionalHeaderClaims,
1270-
string tokenType)
1280+
string tokenType,
1281+
bool includeKeyIdInHeader)
12711282
{
12721283
CryptoProviderFactory cryptoProviderFactory = encryptingCredentials.CryptoProviderFactory ?? encryptingCredentials.Key.CryptoProviderFactory;
12731284

@@ -1281,7 +1292,7 @@ internal static string EncryptToken(
12811292
if (encryptionProvider == null)
12821293
throw LogHelper.LogExceptionMessage(new SecurityTokenEncryptionFailedException(LogMessages.IDX14103));
12831294

1284-
byte[] jweHeader = WriteJweHeader(encryptingCredentials, compressionAlgorithm, tokenType, additionalHeaderClaims);
1295+
byte[] jweHeader = WriteJweHeader(encryptingCredentials, compressionAlgorithm, tokenType, additionalHeaderClaims, includeKeyIdInHeader);
12851296
byte[] plainText;
12861297
if (!string.IsNullOrEmpty(compressionAlgorithm))
12871298
{
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.get -> bool
2+
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.set -> void

src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.ComponentModel;
67
using System.Security.Claims;
78
using System.Threading;
89

@@ -105,5 +106,15 @@ public class SecurityTokenDescriptor
105106
/// values will be overridden.
106107
/// </summary>
107108
public ClaimsIdentity Subject { get; set; }
109+
110+
/// <summary>
111+
/// Indicates if <c>kid</c> and <c>x5t</c> should be included in the header of a JSON web token (JWT)
112+
///
113+
/// <remarks>
114+
/// Only applies to JWTs
115+
/// </remarks>
116+
/// </summary>
117+
[DefaultValue(true)]
118+
public bool IncludeKeyIdInHeader { get; set; } = true;
108119
}
109120
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
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
2+
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

src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,13 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
131131
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
132132
/// <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>
133133
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
134-
public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, string> outboundAlgorithmMap, string tokenType, IDictionary<string, object> additionalInnerHeaderClaims)
134+
/// <param name="includeKeyIdInHeader">Controls if key identifying information should be stored in the header</param>
135+
internal JwtHeader(
136+
SigningCredentials signingCredentials,
137+
IDictionary<string, string> outboundAlgorithmMap,
138+
string tokenType,
139+
IDictionary<string, object> additionalInnerHeaderClaims,
140+
bool includeKeyIdInHeader)
135141
: base(StringComparer.Ordinal)
136142
{
137143
if (signingCredentials == null)
@@ -144,11 +150,14 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
144150
else
145151
Alg = signingCredentials.Algorithm;
146152

147-
if (!string.IsNullOrEmpty(signingCredentials.Key.KeyId))
148-
Kid = signingCredentials.Key.KeyId;
153+
if (includeKeyIdInHeader)
154+
{
155+
if (!string.IsNullOrEmpty(signingCredentials.Key.KeyId))
156+
Kid = signingCredentials.Key.KeyId;
149157

150-
if (signingCredentials is X509SigningCredentials x509SigningCredentials)
151-
this[JwtHeaderParameterNames.X5t] = Base64UrlEncoder.Encode(x509SigningCredentials.Certificate.GetCertHash());
158+
if (signingCredentials is X509SigningCredentials x509SigningCredentials)
159+
this[JwtHeaderParameterNames.X5t] = Base64UrlEncoder.Encode(x509SigningCredentials.Certificate.GetCertHash());
160+
}
152161
}
153162

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

172+
/// <summary>
173+
/// Initializes a new instance of <see cref="JwtHeader"/>.
174+
/// With the Header Parameters:
175+
/// <para>{ { typ, JWT }, { alg, SigningCredentials.Algorithm } }</para>
176+
/// </summary>
177+
/// <param name="signingCredentials"><see cref="SigningCredentials"/> used when creating a JWS Compact JSON.</param>
178+
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
179+
/// <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>
180+
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
181+
public JwtHeader(
182+
SigningCredentials signingCredentials,
183+
IDictionary<string, string> outboundAlgorithmMap,
184+
string tokenType,
185+
IDictionary<string, object> additionalInnerHeaderClaims)
186+
: this(signingCredentials, outboundAlgorithmMap, tokenType, additionalInnerHeaderClaims, true)
187+
{ }
188+
163189
/// <summary>
164190
/// Initializes a new instance of <see cref="JwtHeader"/>.
165191
/// With the Header Parameters:
@@ -196,8 +222,14 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
196222
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
197223
/// <param name="tokenType"> provides the token type</param>
198224
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
225+
/// <param name="includeKeyIdInHeader">Controls if key identifying information should be stored in the header</param>
199226
/// <exception cref="ArgumentNullException">If 'encryptingCredentials' is null.</exception>
200-
public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string, string> outboundAlgorithmMap, string tokenType, IDictionary<string, object> additionalHeaderClaims)
227+
internal JwtHeader(
228+
EncryptingCredentials encryptingCredentials,
229+
IDictionary<string, string> outboundAlgorithmMap,
230+
string tokenType,
231+
IDictionary<string, object> additionalHeaderClaims,
232+
bool includeKeyIdInHeader)
201233
: base(StringComparer.Ordinal)
202234
{
203235
if (encryptingCredentials == null)
@@ -221,7 +253,7 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
221253
// needs to be maintained.
222254
if (AppContextSwitches.UseRfcDefinitionOfEpkAndKid)
223255
{
224-
if (!string.IsNullOrEmpty(encryptingCredentials.KeyExchangePublicKey.KeyId))
256+
if (includeKeyIdInHeader && !string.IsNullOrEmpty(encryptingCredentials.KeyExchangePublicKey.KeyId))
225257
Kid = encryptingCredentials.KeyExchangePublicKey.KeyId;
226258

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

@@ -243,6 +275,24 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
243275
EncryptingCredentials = encryptingCredentials;
244276
}
245277

278+
/// <summary>
279+
/// Initializes a new instance of <see cref="JwtHeader"/>.
280+
/// With the Header Parameters:
281+
/// <para>{ { typ, JWT }, { alg, EncryptingCredentials.Algorithm } }</para>
282+
/// </summary>
283+
/// <param name="encryptingCredentials"><see cref="EncryptingCredentials"/> used when creating a JWS Compact JSON.</param>
284+
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
285+
/// <param name="tokenType"> provides the token type</param>
286+
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
287+
/// <exception cref="ArgumentNullException">If 'encryptingCredentials' is null.</exception>
288+
public JwtHeader(
289+
EncryptingCredentials encryptingCredentials,
290+
IDictionary<string, string> outboundAlgorithmMap,
291+
string tokenType,
292+
IDictionary<string, object> additionalHeaderClaims)
293+
: this(encryptingCredentials, outboundAlgorithmMap, tokenType, additionalHeaderClaims, true)
294+
{ }
295+
246296
/// <summary>
247297
/// Gets the signature algorithm that was used to create the signature.
248298
/// </summary>

0 commit comments

Comments
 (0)