UnderstandingWebFlowExecution

UnderstandingWebFlowExecution

Part of troubleshooting the IdP sometimes requires understanding the basics of how Spring Web Flow operates and how the browser and server work in concert to track the state of each request. When things go sideways, it’s often due to this coordination breaking because of cookie problems in the browser and understanding how it should work can be important when debugging things.

The Basics

Each request starts something called a “flow conversation” with the IdP. This is represented by two values, an execution number and a state number. Each is only unique with respect to a particular Java container session (the session indexed by the JSESSIONID cookie issued by Jetty or any other servlet container). The two numbers are combined into something called a “flow execution key” and this is carried by the query string parameter named “execution” in the form eNsN.

Every round trip between the client and server and back will advance the state number while retaining the same execution number. Issuing a fresh request to the IdP within the same container session will issue a new execution number and reset the state number. So you might see “e1s4” in the midst of logging in, while a new request in another tab might have “e2s1”.

Notably, if you delete the JSESSIONID cookie, or if you switch browsers, that same execution key will NOT work or allow you to resume the same conversation.

When Things Go Wrong

There are a lot of ways ot break this and trigger an exception. Many of them are detected by the IdP and handled with a very generic “Stale Request” error message that covers a lot of bases.

  • Submitting an execution key that doesn’t match a conversation in the active session or submitting the key without the JSESSIONID value issued by the container will cause a NoSuchFlowExecutionException or NoSuchConversationException.

  • Submitting an execution key from an active conversation whose current state value is not a match for the key’s value will cause some form of FlowExecutionException as it detects the out of sync request. (This is what happens when you hit the back button.)

Most issues devolve into one of those two cases at a technical level.

Why is it Breaking?

This is the million dollar question. If a browser is hitting the Stale Request page for seemingly no reason, and there’s no other sort of obvious error in the log indicating a problem with the HTTP method, a replay issue, clock sync, etc., and there’s a flow exception, then usually what’s really going on has to do with cookies. In most cases like this, what’s happening is that the JSESSIONID cookie is being mishandled by the browser in some way and so the second request in the conversation doesn’t carry the cookie issued by the server with the first request, causing the second one to be treated as invalid.

To diagnose this case for sure, you either need to be able to reproduce it yourself (in which case you can observe the cookies being set and sent in each request yourself) or more often you may need to able to observe it in the log. To that end, you will need to include the JSESSIONID value in the log using the IdP’s MDC field (“idp.jsessionid”) as described in the LoggingConfigurationtopic.

Assuming that’s done, you should be able to correlate requests across time and in the web access log and observe the cookie value changing or being absent across very short windows of time. It can be very difficult to tell this if you can’t just trace the activity yourself because by definition the requests aren’t being handled correctly.