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

Trigger metadata refresh for token decryption errors #3149

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
6 changes: 4 additions & 2 deletions src/Microsoft.IdentityModel.Tokens/TokenUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ internal static bool IsRecoverableException(Exception exception)
{
return exception is SecurityTokenInvalidSignatureException
|| exception is SecurityTokenInvalidIssuerException
|| exception is SecurityTokenSignatureKeyNotFoundException;
|| exception is SecurityTokenSignatureKeyNotFoundException
|| exception is SecurityTokenDecryptionFailedException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is sufficient. We should signal metadata refresh only if encryption keys are part of metadata.

}

/// <summary>
Expand Down Expand Up @@ -297,7 +298,8 @@ internal static bool IsRecoverableExceptionType(Type exceptionType)
{
return exceptionType == typeof(SecurityTokenInvalidSignatureException) ||
exceptionType == typeof(SecurityTokenInvalidIssuerException) ||
exceptionType == typeof(SecurityTokenSignatureKeyNotFoundException);
exceptionType == typeof(SecurityTokenSignatureKeyNotFoundException) ||
exceptionType == typeof(SecurityTokenDecryptionFailedException);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.IdentityModel.TestUtils;
using Microsoft.IdentityModel.Tokens;
using Xunit;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace Microsoft.IdentityModel.JsonWebTokens.Tests
{
Expand All @@ -32,68 +33,81 @@ public static TheoryData<ValidateTokenAsyncDecryptionTheoryData> ValidateTokenAs
{
get
{
var theoryData = new TheoryData<ValidateTokenAsyncDecryptionTheoryData>();

theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_Aes128Cbc_HmacSha256")
var theoryData = new TheoryData<ValidateTokenAsyncDecryptionTheoryData>
{
EncryptingCredentials = new EncryptingCredentials(
new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_Aes128Cbc_HmacSha256")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509Key_2048),
ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509Key_2048),
});
TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509Key_2048),
ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509Key_2048),
},

#if NET472 || NET6_0_OR_GREATER
theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_EcdhEs")
{
EncryptingCredentials = new EncryptingCredentials(
new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_EcdhEs")
{
EncryptingCredentials = new EncryptingCredentials(
new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true),
SecurityAlgorithms.EcdhEsA256kw,
SecurityAlgorithms.Aes128CbcHmacSha256)
{
KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public
{
KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public
},
AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP521_Public),
TokenValidationParameters = CreateTokenValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
ValidationParameters = CreateValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
},
AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP521_Public),
TokenValidationParameters = CreateTokenValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
ValidationParameters = CreateValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
});
#endif

theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_NoDecryptionKeys")
{
EncryptingCredentials = new EncryptingCredentials(
new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_NoDecryptionKeys")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
TokenValidationParameters = CreateTokenValidationParameters(),
ValidationParameters = CreateValidationParameters(),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"),
});

theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_WrongDecryptionKey")
{
EncryptingCredentials = new EncryptingCredentials(
TokenValidationParameters = CreateTokenValidationParameters(),
ValidationParameters = CreateValidationParameters(),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"),
},
new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_WrongDecryptionKey")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1),
ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
// Avoid comparing the full exception message as the stack traces for the inner exceptions are different.
ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
},
new ValidateTokenAsyncDecryptionTheoryData("JWE_TokenDecryptError_SuccessOnRetry")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1),
ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
// Avoid comparing the full exception message as the stack traces for the inner exceptions are different.
ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
});
TokenValidationParameters = CreateTokenValidationParameters(configurationManager: CreateConfigurationManager()),
ValidationParameters = CreateValidationParameters(configurationManager: CreateConfigurationManager()),
ExpectedIsValid = true,
},
};

return theoryData;

static TokenValidationParameters CreateTokenValidationParameters(
SecurityKey? tokenDecryptionKey = null, bool tryAllKeys = false)
SecurityKey? tokenDecryptionKey = null,
bool tryAllKeys = false,
BaseConfigurationManager? configurationManager = null)
{
// Skip all validations. We just want to decrypt the JWE.
var tokenValidationParameters = new TokenValidationParameters
{
ConfigurationManager = configurationManager,
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = false,
Expand All @@ -106,13 +120,16 @@ static TokenValidationParameters CreateTokenValidationParameters(
return tokenValidationParameters;
}

static ValidationParameters CreateValidationParameters(SecurityKey? tokenDecryptionKey = null)
static ValidationParameters CreateValidationParameters(
SecurityKey? tokenDecryptionKey = null,
BaseConfigurationManager? configurationManager = null)
{
ValidationParameters validationParameters = new ValidationParameters();

if (tokenDecryptionKey is not null)
validationParameters.TokenDecryptionKeys = [tokenDecryptionKey];

validationParameters.ConfigurationManager = configurationManager;

// Skip all validations. We just want to decrypt the JWE
validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation;
Expand All @@ -127,6 +144,16 @@ static ValidationParameters CreateValidationParameters(SecurityKey? tokenDecrypt
return validationParameters;
}

static BaseConfigurationManager CreateConfigurationManager()
{
var configNoDecryptKeys = new OpenIdConnectConfiguration();
var configWithDecryptKeys = new OpenIdConnectConfiguration();
configWithDecryptKeys.TokenDecryptionKeys.Add(KeyingMaterial.DefaultX509Key_2048);
var configManager = new MockConfigurationManager<OpenIdConnectConfiguration>(configNoDecryptKeys);
configManager.RefreshedConfiguration = configWithDecryptKeys;

return configManager;
}

#if NET472 || NET6_0_OR_GREATER
static Dictionary<string, object> AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3257,6 +3257,13 @@ public static TheoryData<JwtTheoryData> ValidateJweTestCases
{
var handlerWithNoDefaultTimes = new JsonWebTokenHandler();
handlerWithNoDefaultTimes.SetDefaultTimesOnTokenCreation = false;

var configNoDecryptKeys = new OpenIdConnectConfiguration();
var configWithDecryptKeys = new OpenIdConnectConfiguration();
configWithDecryptKeys.TokenDecryptionKeys.Add(KeyingMaterial.DefaultX509Key_2048);
var configManager = new MockConfigurationManager<OpenIdConnectConfiguration>(configNoDecryptKeys);
configManager.RefreshedConfiguration = configWithDecryptKeys;

return new TheoryData<JwtTheoryData>
{
new JwtTheoryData
Expand Down Expand Up @@ -3345,6 +3352,22 @@ public static TheoryData<JwtTheoryData> ValidateJweTestCases
AlgorithmValidator = ValidationDelegates.AlgorithmValidatorBuilder(true)
},
},
new JwtTheoryData
{
TestId = "JWE_TokenDecryptError_SuccessOnRetry",
Token = new JsonWebTokenHandler().CreateToken(
Default.PayloadString,
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
new EncryptingCredentials(KeyingMaterial.DefaultX509Key_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256)),
ValidationParameters = new TokenValidationParameters
{
ValidAudience = Default.Audience,
ValidIssuer = Default.Issuer,
ValidateIssuerSigningKey = true,
IssuerSigningKey = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
ConfigurationManager = configManager,
}
},
};
}
}
Expand Down
Loading