Solving Correlation Failed: State Property Not Found Errors (OpenID Connect Middleware / ASP.NET Core)

Recently we ran into an issue at one of my clients when integrating an ASP.NET Core application with an IDP using the OpenID Connect middleware.  When running the application locally everything worked as expected: the middleware takes care of most of the hard work as far as generating the calls the authorization & token endpoints, validating the tokens, signing in, … is concerned.  But when deploying the solution, we ran into “Correlation failed” errors.  More specifically: the exception stated “State property not found”.  The environment we were deploying to was of course different from a local dev environment.  As is the case in most companies, we’re talking reverse proxy, load balancers, …

After some Googling it seemed like we weren’t the only ones that ran into this issue, but none of the suggestions in those posts seemed to (consistently) work. 

So we enabled additional logging, dived into the source code and found out that the culprit was the StateDataFormat.Unprotect(message.State) call which failed.  In case you’re not familiar with this: this is part of ASP.NET Core’s new Data Protection APIs, and the middleware uses these.  In short: state is encrypted by the middleware before sending it to the IDP.  When the IDP posts back to the web app, it posts back the encrypted state, which must then be decrypted by the middleware for further use.

Decryption failed, resulting in a null state, which then resulted in that “Correlation failed: state property not found” error.

Turns out the middleware tried to decrypt the state with a different key than the one that was used for encryption.  By default, the keys are stored in the %LOCALAPPDATA% location on the server.  When you’re behind a load balancer, requests have tendency to end up on different servers, thus resulting in different keys being used. Once we were sure of the reason, the solution was easy: we stored the keys on a file system share instead of on each server. 


public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
}

Hope this helps some of you out.  Happy coding! :)

 Tweet about this on TwitterShare on LinkedInShare on Facebook

6 Comments

john arastoozad

I had the same issue. I was defining multiple external endpoints for Authorization.
In my case I had defined Callback Paths that were being used by multiple clients.
Once I defined unique Callback Paths the problem was solved:
example:
options.Authority = …..”;
. .
options.CallbackPath = “/signin-idsrv2”; // I already had /sign-in-idsrv
Similarly, make sure the SignedOutCallbackPaths are unique. Hope it works for you.

Reply
James Paull

Excellent article, Kevin. But I have a question…

We are seeing this behavior in our test environment which has a load balancer but currently only one server behind it. Any ideas on why that would be happening?

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *