Description
Here's how it happens:
- A request comes in, with a cookie. Cookie handler needs to decrypt the cookie, this ultimately results in a call to
KeyRingBasedDataProtector.UnprotectCore()
UnprotectCore
callsKeyRingProvider.GetCurrentKeyRingCore
to get keys. The latter determines that it needs to refresh the key ring from the repository.- The repository experiences some transient failure and throws an exception. At this time the
KeyRingProvider
does have the key ring available, so it extends its lifetime for 2 minutes and reports that from here - Now, instead of just returning the old key ring with freshly extended lifetime,
KeyRingProvider
re-throws the exception. Rationale for that is described here - The problem is,
KeyRingBasedDataProtector.UnprotectCore
does not retry. If it did, it would get the key ring, because it's lifetime was just extended. But it just doesn't make that second call toGetCurrentKeyRing
- So, the cookie is not decrypted, authentication handler fails and the error leaks to the caller as a 500 response, which is completely preventable.
Here's a stack trace for your reference
at Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.GetAllKeys()
at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider.CreateCacheableKeyRingCore(DateTimeOffset now, IKey keyJustAdded)
at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider.Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.ICacheableKeyRingProvider.GetCacheableKeyRing(DateTimeOffset now)
at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider.GetCurrentKeyRingCore(DateTime utcNow, Boolean forceRefresh)
at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status)