Skip to Content
0

OData Provisioning - CSRF Token

Jun 01, 2017 at 04:55 PM

242

avatar image

Dear all,

I tried to publish an on-premise OData service through the OData Provisioning service of the Cloud Platform. The backend is connected via Cloud Connector.

The overall setup works fine, although I am now facing an issue with the good old CSRF Token... I checked other questions, especially this one, but it didn't work for me.

The final goal is to interface an AWS Lambda Function (based on Node.js) with the service exposed by the OData Provisioning. Knowing that a CSRF token is needed to perform update requests, here's what I am doing:

var createAbsence = function (oAbsenceData, fCallback) {


    async.waterfall(
        [
            // i. Fetch the HTTP CSRF Token Header:
            function (asyCallback) {
                client.get(sEndpoint)
                    .auth(sUsername, sPassword, true)
                    .header('X-CSRF-Token', 'Fetch')
                    .end(function (oResponse) {
                        if (oResponse.code == 200) {
                            asyCallback(null, oResponse.headers['x-csrf-token']);
                        } else {
                            asyCallback('Unable to retrieve CSRF Token', null);
                        }
                    });
            },


            // ii. Perform the Post Request:
            function (token, asyCallback) {
                var request = client.post(sEndpoint);
                request.auth(sUsername, sPassword, true)
                       .header('X-CSRF-Token', token)
                       .header('Accept', 'application/json')
                       .type('json').send(convertAbsenceData(oAbsenceData));
                request.end(function (oResponse) {
                    if (oResponse.code == 201) {
                        asyCallback(null, 'Big Success!');
                    } else {
                        console.log(oResponse.code);
                        asyCallback("Expecting 201, got " + oResponse.code, null);
                    }
                });
            }
        ],


        // Final callback
        function (err, status) {
            if (err) {
                fCallback(status, "Something went wrong");
            } else {
                fCallback(status, "All good!");
            }
        }
    );
}

Encapsulated in an async.waterfall chain, I first trigger a GET request, asking for the CSRF Token (x-csrf-token: Fetch). This is working fine, an the token gets generated by the backend.

Secondly, I trigger the POST request reusing the token generated before: no matter what, I get a 403 HTTP response.

The very strange thing is that if I replicate these exact steps using a REST Client like Postman, everything works well!

GET Request:

Then, after pasting the token into the POST Request:

As you can see, I get 201 - Created.

Anyone faced a similar situation? Can it be fixed somehow?

Thanks,

Roberto.

10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

1 Answer

Best Answer
Roberto Pagni
Jun 02, 2017 at 12:12 AM
1

In the end the solution was very similar to the one posted in the question I was referring.

I was able to fix this by providing session cookies created by the first GET call to the second POST call, using a cookie jar.

By forcing the creation of a persistent cookie jar, and then providing this jar to http calls, cookies will be shared across calls and the CSRF token gets validated.

// Create a cookie jar with persistance, in order to share session cookies across
// requests -- this does the trick
var cookieJar   = client.jar(true);

// Wrapper function for posting the absence.
var createAbsence = function (oAbsenceData, fCallback) {

    async.waterfall(
        [
            // i. Fetch the HTTP CSRF Token Header:
            function (asyCallback) {
                console.log("Requesting CSRF Token...");
                client.get(sEndpoint).auth(sUsername, sPassword, true)
                    .header('X-CSRF-Token', 'Fetch')
                    .header('Connection', 'keep-alive')
                    .jar(cookieJar)
                    .end(function (oResponse) {
                        if (oResponse.code == 200) {
                            console.log("Got CSRF Token", oResponse.headers['x-csrf-token']);
                            asyCallback(null, oResponse.headers['x-csrf-token']);
                        } else {
                            console.log("Expecting 200, got", oResponse.code);
                            asyCallback('Unable to retrieve CSRF Token', null);
                        }
                    });
            },

            // ii. Perform the Post Request:
            function (token, asyCallback) {
                console.log("Posting Absence...");
                client.post(sEndpoint).auth(sUsername, sPassword, true)
                       .jar(cookieJar)
                       .header('X-CSRF-Token', token)
                       .header('Accept', 'application/json')
                       .type('json').send(convertAbsenceData(oAbsenceData))
                       .end(function (oResponse) {
                    if (oResponse.code == 201) {
                        console.log("Backend System responded with code", oResponse.code);
                        asyCallback(null, oResponse.code, " - Created");
                    } else {
                        console.log("Backend System responded with code", oResponse.code);
                        asyCallback("Expecting 201, got " + oResponse.code, oResponse.code, JSON.stringify(oResponse.body));
                    }
                });
            }
        ],
Share
10 |10000 characters needed characters left characters exceeded