ConfidentialClientApplication.AcquireTokenForClient fails with Microsoft.Identity.Client.MsalClientException: MSAL V3 Deserialization failed
See original GitHub issueLogs and network traces Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs and network traces is described in Logging wiki.
Which version of MSAL.NET are you using? MSAL.NET 4.40.0.0
Platform .NET 4.7
What authentication flow has the issue?
- Desktop / Mobile
- Interactive
- Integrated Windows Authentication
- Username Password
- Device code flow (browserless)
- Web app
- Authorization code
- On-Behalf-Of
- Daemon app
- Service to Service calls
Other?
Using token cache serialization from Microsoft.Identity.Web.TokenCache 1.22.1
- CCA is configured to use DistributedCache using the
AddDistributedTokenCache
method MsalDistributedTokenCacheAdapterOptions
hasEncrypt=true
- Keys are protected using
ProtectKeysWithCertificate
and shared via Redis - The flow was working till the DataProtectionCertificate used ^^ was rotated. (bug -> https://github.com/AzureAD/microsoft-identity-web/issues/1575)
Looking at the stack trace (attached file) and the code, it looks like DeserializeMsalV3
fails when UnprotectBytes
is unable to decrypt the token in Microsoft.Identity.Web.TokenCache, it still tries to deserialize encrypted token and fails as that is not a valid json string. Link to code:
Is this a new or existing app? a. The app is in production, and I have upgraded to a new version of MSAL and the DataProtectionCertificate was rotated.
Repro
- Enable Distributed cache with encryption using Redis.
- Rotate the certificate used to protect token encryption keys.
- Code for building/configuring ConfidentialClientApplication:
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(appId)
.WithCertificate(clientCertificate)
.WithAuthority(authorityUri)
.WithAzureRegion()
.WithCacheSynchronization(false)
.WithLegacyCacheCompatibility(false)
.Build();
app.AddDistributedTokenCache(services =>
{
// Adding data protections helps secure the keys that are used to encrypt the token cache.
services.AddDataProtection(o =>
{
o.ApplicationDiscriminator = DataProtectionApplicationDiscriminator;
})
// Key material needs to be shared so it can be available to different machines/processes.
// Using Redis to store the keys and then encrypt these keys with the X509 data encryption certificate.
.PersistKeysToStackExchangeRedis(
() => dataProtection.Cache.GetDatabase(),
dataProtection.KeyName)
.ProtectKeysWithCertificate(dataProtection.EncryptionCertificate);
services.Configure<MsalDistributedTokenCacheAdapterOptions>(o =>
{
o.Encrypt = true;
o.OnL2CacheFailure = (e) =>
{
// Retry cache operation? false - no, true - yes.
return true;
};
})
.AddStackExchangeRedisCache(o =>
{
o.ConfigurationOptions = dataProtection.Cache.ConfigurationOptions;
});
});
Expected behavior
AcquireTokenForClient
should not fail on L1/L2 cache read and/or write failure and should return access token directly from AAD.
Actual behavior
AcquireTokenForClient
fails with MSALClientException MSAL V3 Deserialization failed
.
stack trace
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (3 by maintainers)
Top GitHub Comments
@fahd-ms Specifically for the issue related to key encryption certificates, Data Protection API has UnprotectKeysWithAnyCertificate method that can accept new and old certs. Have you tried this? For a more general issue of deserialize failing, we’re investigating a better way of handling the exception.
released in 1.23.1