Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This scenario can be detected by attaching a unique identifier to each request, a randomly generated GUID we call a request-ward. For a given session, the Runtime always knows what is the next expected request-ward and can detect a de-synchronized request, as in the following example:

 

 

StepActorDescription
1User/BrowserThe User clicks on button1. A request to handle the event is sent, together with the current request-ward
2Load BalancerThe load balancer forwards the request to Runtime 1.
3Runtime 1The Runtime reads the session from the key-value store.
4Runtime 1The Runtime validates the request ward, generates a new request ward and handles the event. The session is now on Page 2
5Runtime 1The Runtime writes the session in the key-value store. The new request ward is also saved as part of the session.
6.Runtime 1The Runtime crashes before being able to send the response.
7Load BalancerThe load balancer re-sends the requests to Runtime 2
8Runtime 2The Runtime reads the session from the key-value store, validates the request ward and sees that the received ward (ward1) doesn't match the expected ward (ward2)
9Runtime 2The Runtime response with an HTTP 400 Bad Request and a JSON in the response body indicating that an invalid request ward was received
10Load BalancerThe load balancer sends the response back to the User. The user sees an error message stating that the page should be refreshed.
11User/BrowserThe User refreshes the page. This consists of a request to the current UI (for example: /session/{sessionId}/mvc/index.html when MVC UI is used)
12Load BalancerThe load balancer forwards the request to Runtime 2
13Runtime 2The Runtime reads the session from the key-value store
14Runtime 2The Runtime generates the current page model (for Page 2) and sends it in the response. The current request ward (ward2) is also send as part of the response
15Load BalancerThe load balancer forwards the response to the User. The user now correctly sees that he/she is on Page 2.
16User/BrowserThe user clicks another button on Page 2. A request to handle the event is sent, together with the new request ward.
17Load BalancerThe load balancer fowards the request to Runtime 2
18Runtime 2The Runtime reads the session from the key-value store and validates the request ward. This time, the request ward is valid.
19Runtime 2The Runtime generates a new request ward (ward3) and handles the event
20Runtime 2The Runtime writes the session in the key-value store, together with the new request ward
21Runtime 2The Runtime response with the page changes and the new request ward
22Load BalancerThe load balancer forwards the response to the User. The user correctly sees the changes on the page, and also has the new request ward for the next request.

 

Please note that while the request ward is similar to a CSRF token, it differs in a few key areas:

...

  • /Runtime/server/session/{sessionId}/load - this endpoint loads the page model. Request ward validation is disabled for this endpoint, as the front-end doesn't yet know the request ward. Request ward renewal is enabled however.
  • /Runtime/server/session/{sessionId}/keepalive - this endpoint extends the session timeout. Request ward validation and renewal is disabled for this endpoint as keep-alive requests may be performed in parallel with other modification requests for the same session
  • /Runtime/server/session/{sessionId}/close - this endpoint closes the session. Request ward validation and renewal is disabled for this endpoint as session de-synchronization is not possible. If the session is deleted and then a failover occurs, then repeating the request on the backup node will result in an error (because the session is already deleted).

The response in case of an invalid request ward has status code 400 and a JSON in the response body like in the following example:

Code Block
{
  "type":"INVALID_REQUEST_WARD",
  "title":"Invalid Request",
  "message":"Please refresh the page"
}

The title and message are internationalized and can be configured in the usual way in messages.properties. The language of the request is determined based on the Accept-Language request header.

The default MVC v2 front-end implementation just displays this message to the user. Custom implementations may use a combination of the response status code and type field to detect this exception and automatically reload the page. However, notifying the user is recommended, so the user has a change to take note of the data entered on the page which will be lost once the page is reloaded.

Multipart Requests

For multipart requests the request ward is also accepted as a request parameter named X-Request-Ward. This exception is made in order to accomodate older browsers which do not support true AJAX multipart requests / file uploads an require the use of hidden forms and iframes to simulate AJAX file uploads. The request ward is accepted as a parameter only for multi-part requests. If both the header and parameter are present, the header takes precedence. The same rules as for regular requests apply as well.

Configuration

Request wards can be enabled or disabled globally with the following property in application.properties:

Code Block
blueriq.session.request-ward-enabled=false

By default, request wards are disabled, as Dashboards do not work with request wards enabled due to a few endpoints that accept parallel requests for the same session. 

The error messages may be internationalized in messages.properties and message_<language>-<country>.properties using the following keys:

Code Block
request-ward.invalid.title=Invalid request
request-ward.invalid.message=Please refresh the page