diff --git a/identity-server/nuget.config b/identity-server/nuget.config
deleted file mode 100644
index 54e660f9c..000000000
--- a/identity-server/nuget.config
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/identity-server/src/IdentityServer/Validation/Default/PrivateKeyJwtSecretValidator.cs b/identity-server/src/IdentityServer/Validation/Default/PrivateKeyJwtSecretValidator.cs
index 354c932a1..09bbc5bf4 100644
--- a/identity-server/src/IdentityServer/Validation/Default/PrivateKeyJwtSecretValidator.cs
+++ b/identity-server/src/IdentityServer/Validation/Default/PrivateKeyJwtSecretValidator.cs
@@ -96,13 +96,14 @@ public async Task ValidateAsync(IEnumerable secr
// Read the token so we can get the "typ" header value if it exists.
var handlerForHeader = new JsonWebTokenHandler();
var tokenForHeader = handlerForHeader.ReadJsonWebToken(jwtTokenString);
- var jwtTyp = tokenForHeader.GetHeaderValue("typ");
-
// If strict mode is not enabled by option but the "typ" header value "client-authentication+jwt" is provided,
// enforce strict audience validation.
- if (string.Equals(jwtTyp, "client-authentication+jwt", StringComparison.OrdinalIgnoreCase))
+ if (tokenForHeader.TryGetHeaderValue("typ", out var jwtTyp))
{
- enforceStrictAud = true;
+ if (string.Equals(jwtTyp, "client-authentication+jwt", StringComparison.OrdinalIgnoreCase))
+ {
+ enforceStrictAud = true;
+ }
}
}
catch (Exception ex)
@@ -252,4 +253,4 @@ private bool AudiencesMatchIgnoringTrailingSlash(string tokenAudience, string va
return false;
}
-}
\ No newline at end of file
+}
diff --git a/identity-server/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs b/identity-server/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs
index fb28c538c..d6852bf23 100644
--- a/identity-server/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs
+++ b/identity-server/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs
@@ -44,17 +44,24 @@ public PrivateKeyJwtSecretValidation()
_clients = new InMemoryClientStore(ClientValidationTestClients.Get());
}
- private JwtSecurityToken CreateToken(string clientId, string aud = "https://idsrv.com/", DateTime? nowOverride = null, bool setTyp = false)
+ private JwtSecurityToken CreateToken(string clientId, string aud = "https://idsrv.com/", DateTime? nowOverride = null, Typ typ = Typ.None)
{
- return CreateTokenHelper(clientId, new Claim(JwtClaimTypes.Audience, aud), nowOverride, setTyp);
+ return CreateTokenHelper(clientId, new Claim(JwtClaimTypes.Audience, aud), nowOverride, typ);
}
- private JwtSecurityToken CreateToken(string clientId, string[] audiences, DateTime? nowOverride = null, bool setTyp = false)
+ private JwtSecurityToken CreateToken(string clientId, string[] audiences, DateTime? nowOverride = null, Typ typ = Typ.None)
{
- return CreateTokenHelper(clientId, new Claim(JwtClaimTypes.Audience, JsonSerializer.Serialize(audiences), JsonClaimValueTypes.JsonArray), nowOverride, setTyp);
+ return CreateTokenHelper(clientId, new Claim(JwtClaimTypes.Audience, JsonSerializer.Serialize(audiences), JsonClaimValueTypes.JsonArray), nowOverride, typ);
}
- private JwtSecurityToken CreateTokenHelper(string clientId, Claim aud = null, DateTime? nowOverride = null, bool setJwtTyp = false)
+ public enum Typ
+ {
+ None,
+ JWT,
+ ClientAuthentication
+ }
+
+ private JwtSecurityToken CreateTokenHelper(string clientId, Claim aud = null, DateTime? nowOverride = null, Typ jwtTyp = Typ.None)
{
var certificate = TestCert.Load();
var now = nowOverride ?? DateTime.UtcNow;
@@ -80,10 +87,18 @@ private JwtSecurityToken CreateTokenHelper(string clientId, Claim aud = null, Da
)
);
- if (setJwtTyp)
+ if (jwtTyp == Typ.ClientAuthentication)
{
token.Header["typ"] = "client-authentication+jwt";
}
+ else if (jwtTyp == Typ.JWT)
+ {
+ token.Header["typ"] = "JWT";
+ }
+ else
+ {
+ token.Header.Remove("typ");
+ }
return token;
}
@@ -144,12 +159,17 @@ public async Task Valid_Certificate_Base64()
}
[Theory]
- [InlineData("https://idsrv.com")]
- [InlineData("https://idsrv.com/")]
- [InlineData("https://idsrv.com/connect/token")]
- [InlineData("https://idsrv.com/connect/ciba")]
- [InlineData("https://idsrv.com/connect/par")]
- public async Task Strict_audience_disabled_and_no_typ_header_allows_legacy_aud_values(string aud)
+ [InlineData("https://idsrv.com", Typ.None)]
+ [InlineData("https://idsrv.com/", Typ.None)]
+ [InlineData("https://idsrv.com/connect/token", Typ.None)]
+ [InlineData("https://idsrv.com/connect/ciba", Typ.None)]
+ [InlineData("https://idsrv.com/connect/par", Typ.None)]
+ [InlineData("https://idsrv.com", Typ.JWT)]
+ [InlineData("https://idsrv.com/", Typ.JWT)]
+ [InlineData("https://idsrv.com/connect/token", Typ.JWT)]
+ [InlineData("https://idsrv.com/connect/ciba", Typ.JWT)]
+ [InlineData("https://idsrv.com/connect/par", Typ.JWT)]
+ public async Task Strict_audience_disabled_and_no_typ_header_or_JWT_typ_header_allows_legacy_aud_values(string aud, Typ typ)
{
_options.Preview.StrictClientAssertionAudienceValidation = false;
@@ -159,7 +179,7 @@ public async Task Strict_audience_disabled_and_no_typ_header_allows_legacy_aud_v
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, aud: aud, setTyp: false)),
+ Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, aud: aud, typ: typ)),
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -185,7 +205,7 @@ public async Task Strict_audience_from_options_validates_audience(string aud, bo
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, aud: aud, setTyp: true)),
+ Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, aud: aud, typ: Typ.ClientAuthentication)),
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -211,7 +231,7 @@ public async Task Strict_audience_from_typ_header_validates_audience(string aud,
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, aud: aud, setTyp: true)),
+ Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, aud: aud, typ: Typ.ClientAuthentication)),
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -221,11 +241,13 @@ public async Task Strict_audience_from_typ_header_validates_audience(string aud,
}
[Theory]
- [InlineData(true, true, false)]
- [InlineData(true, false, false)]
- [InlineData(false, true, false)]
- [InlineData(false, false, true)]
- public async Task Strict_audience_does_not_allow_single_valued_arrays(bool setTyp, bool setStrictOption, bool expectedResult)
+ [InlineData(Typ.ClientAuthentication, true, false)]
+ [InlineData(Typ.ClientAuthentication, false, false)]
+ [InlineData(Typ.None, true, false)]
+ [InlineData(Typ.None, false, true)]
+ [InlineData(Typ.JWT, true, false)]
+ [InlineData(Typ.JWT, false, true)]
+ public async Task Strict_audience_does_not_allow_single_valued_arrays(Typ typ, bool setStrictOption, bool expectedResult)
{
_options.Preview.StrictClientAssertionAudienceValidation = setStrictOption;
@@ -234,7 +256,7 @@ public async Task Strict_audience_does_not_allow_single_valued_arrays(bool setTy
var token = new JwtSecurityTokenHandler().WriteToken(CreateToken(
clientId,
audiences: ["https://idsrv.com/connect/token"],
- setTyp: setTyp));
+ typ: typ));
var secret = new ParsedSecret
{
@@ -249,11 +271,13 @@ public async Task Strict_audience_does_not_allow_single_valued_arrays(bool setTy
}
[Theory]
- [InlineData(true, true, false)]
- [InlineData(true, false, false)]
- [InlineData(false, true, false)]
- [InlineData(false, false, true)]
- public async Task Strict_audience_does_not_allow_multi_valued_arrays(bool setTyp, bool setStrictOption, bool expectedResult)
+ [InlineData(Typ.ClientAuthentication, true, false)]
+ [InlineData(Typ.ClientAuthentication, false, false)]
+ [InlineData(Typ.None, true, false)]
+ [InlineData(Typ.None, false, true)]
+ [InlineData(Typ.JWT, true, false)]
+ [InlineData(Typ.JWT, false, true)]
+ public async Task Strict_audience_does_not_allow_multi_valued_arrays(Typ typ, bool setStrictOption, bool expectedResult)
{
_options.Preview.StrictClientAssertionAudienceValidation = setStrictOption;
@@ -262,7 +286,7 @@ public async Task Strict_audience_does_not_allow_multi_valued_arrays(bool setTyp
var token = new JwtSecurityTokenHandler().WriteToken(CreateToken(
clientId,
audiences: ["https://idsrv.com", "https://idsrv.com/"],
- setTyp: setTyp));
+ typ: typ));
var secret = new ParsedSecret
{
@@ -277,17 +301,19 @@ public async Task Strict_audience_does_not_allow_multi_valued_arrays(bool setTyp
}
[Theory]
- [InlineData(true, true, true)]
- [InlineData(true, false, true)]
- [InlineData(false, true, false)]
- [InlineData(false, false, true)]
- public async Task Strict_audience_only_allows_correct_type(bool setTyp, bool enforceStrict, bool expectedResult)
+ [InlineData(Typ.ClientAuthentication, true, true)]
+ [InlineData(Typ.ClientAuthentication, false, true)]
+ [InlineData(Typ.None, true, false)]
+ [InlineData(Typ.None, false, true)]
+ [InlineData(Typ.JWT, true, false)]
+ [InlineData(Typ.JWT, false, true)]
+ public async Task Strict_audience_only_allows_correct_type(Typ typ, bool enforceStrict, bool expectedResult)
{
_options.Preview.StrictClientAssertionAudienceValidation = enforceStrict;
var clientId = "certificate_base64_valid";
var client = await _clients.FindEnabledClientByIdAsync(clientId);
- var token = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, setTyp: setTyp));
+ var token = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId, typ: typ));
var secret = new ParsedSecret
{
@@ -419,4 +445,4 @@ public async Task Invalid_Unsigned_Token()
result.Success.Should().BeFalse();
}
-}
\ No newline at end of file
+}