Architecture

To explain Raider’s architecture, we’ll be modelling an authentication process.

Abstracting the authentication process

First let’s start by taking a closer look at how web authentication works. Every authentication process can be abstracted as a Finite State Machine.

On a high level, we start in the unauthenticated state, the user sends the application their credentials, optionally the multi-factor authentication (MFA) code, and if both checks pass, we reach the authenticated state. A typical modern web application will looks like the following in a diagram:

../_images/high_level_authentication.png

Basic concepts in Raider

Now let’s zoom in and look at the details. Instead of dealing with the states (Unauthenticated, Login failed, MFA required, and Authenticated), we define the concept of Flows, which describes the information exchange between the client and the server containing one request and the respective response.

The example below shows a closer look of the authentication process for an imaginary web application:

../_images/detailed_authentication.png

To describe the authentication process from the example defined above, we need three stages. The first one, Initialization, doesn’t have any inputs, but creates the Session cookie and the CSRF token as outputs.

Raider Flows

Those outputs are passed to the next stage, Login, together with user credentials. A request is built with those pieces of information, and the new outputs are generated. In this case we have the new CSRF token, an updated session cookie, and a new cookie identifying the user: user cookie.

Depending on whether MFA is enabled or not, the third stage Multi-factor authentication might be skipped or executed. If it’s enabled, the outputs from the previous stage get passed as inputs to this one, the user is asked to input the next Factor, and a new cookie is set proving the user has passed the checks and is properly authenticated.

In Raider, those stages are implemented using Flow objects. The authentication process consists of a series of Flows connected to each other. Each one accepts inputs and generates outputs. In addition to that, Flow objects implement Operations which can be used to run various actions upon receiving the response, but most importantly they’re used to control the authentication process by conditionally or unconditionally defining the next stage. So for example one can jump to stage X if the HTTP response code is 200 or to stage Y if it’s 403.

../_images/raider_flows.png

Inputs and outputs are often the same object, and you may want to update its value from one Flow to the next (for example the CSRF token changes for every stage). This was implemented in Raider using Plugins.

Plugins are pieces of code that can act as inputs for the HTTP requests to be sent, and/or as outputs from the HTTP responses. They are used to facilitate the information exchange between Flows. Raider provides the user the option to write new plugins with a small piece of hylang code.

Once the response is received, the Operations will be executed. The primary function of operations is to define which Flow comes next. But they can do anything, and Raider makes it easy to write new operations.

Graph-like structure

Chaining several Flows together can be used to simulate any stateful HTTP process. FlowGraphs indicate the starting point. They can be placed on any Flow. A FlowGraphs runs all Flows in the link until Success/Failure is returned or if there are no more links.

../_images/graph.png