Skip to Content
avatar image
Former Member

OData Provisioning - CSRF Token

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.

Add comment
10|10000 characters needed characters exceeded

  • Get RSS Feed

1 Answer

  • Best Answer
    avatar image
    Former Member
    Jun 02, 2017 at 12:12 AM

    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));
                        }
                    });
                }
            ],
    
    Add comment
    10|10000 characters needed characters exceeded