$AJAX / JSON / Wheels.InvalidAuthenticityToken ERROR

149 views
Skip to first unread message

Dhg Associates

unread,
Jul 19, 2020, 12:09:22 AM7/19/20
to CFWheels

Using CFWheel 2.0. I have an ajax application that is using ajax get/PATCH calls to my controller.  When I try to do a jquery $ajax "push" (PATCH) request, I get the following error:


Wheels.InvalidAuthenticityToken

This POSTed request was attempted without a valid authenticity token.

Tag context

Error thrown on line 34 in wheels\controller\csrf.cfm
However, in the guide: "Tutorial: CFWheels, AJAX, and You" -- Unfortunately, there is no example of a "PATCH" ajax call.

The following code generates the Ajax Get call:
/* file: getDbItemViaAjax.js */

   
// GET  the    EVENT    record(s)  from the Database
function     getDb_CurEvents() {
   
var params = { format:'json', inventoryitemid:g.curItemNum };
   
var paramsStr = jQuery.param( params );
   
    $
.ajax({
        url
: (g.api_url+'/inventoryevents/?'+ paramsStr )    
       
,
        type
: "GET",
        dataType
: "json",
        contentType
: 'application/json',
        cache
:false,
        timeout
: 20000,
       
        success
: function( rsp ) {
           
// IN     FETCHING__Db_CUR_EVENT__STATE
           
            manageStatesAndTransitions
(
               
"json_rsp_db__CUR_EVENTS"                /*  "event.type" */
               
, rsp
               
);    
       
},

        error
: function( xmlhttprequest, textStatusStr, errorThrownStr  ) {
           
if(textStatusStr=="timeout") {
                alert
("ERROR~286: getDb_CurEvents() timed out");
           
} else {
               
                alert
( "Error~300: getDb_CurEvents()  ${xmlhttprequest}   textStatus=" + textStatusStr + "  errorThrown=" + errorThrownStr);
           
               
if (xmlhttprequest.responseText) {
                   
var subStr = xmlhttprequest.responseText.match("(\<\/header\>)(.*)(\<\/body\>)");
                   
if (subStr !== null) {
                   
                        alert
(subStr[1]);
                        alert
(subStr[2]);
                       
                       
// see https://www.tutorialrepublic.com/faq/how-to-find-substring-between-the-two-words-using-jquery.php
                   
} else {
                        alert
(xmlhttprequest.responseText);
                   
}
               
}
               
           
}            
       
}
   
   
});
}
           



QUESTION : How do I modify the index() function in my CONTROLLER  code below to return a  "_hidden" field containing a Authenticity Token in my ajax return structure?

/* inventoryevents CONTROLLER */

   
/**
    * View all [modelNames]
    *
    * AJAX CALL:
http://www.gleneckguideto.com/aod_inv/inventoryevents/?format=json&inventoryitemid=1
    */

   
function index() {
        LOCAL
.mp= pluralize(this.modelName);
   
       
if(structKeyExists(params, "format") AND (params.format=="json")) {
           /*
            * $AJAX GET: "http://www.gleneckguideto.com/aod_inv/inventoryevents/?format=json&inventoryitemid=1&_=1595128550429?"
            */


            LOCAL
.AjaxResponse = GetNewAjaxResponse();
           
           
if(structKeyExists(params, "inventoryitemid")) {

                 
// ORIGINAL CODE    
                LOCAL
.inventoryevent=  model("inventoryevent").findAllByInventoryitemid(
                      value
=params.inventoryitemid
                   
, order = "asofdate,id"
                   
, returnAs="structs"
               
);

                // LOCAL.AjaxResponse.token = "";
                LOCAL
.AjaxResponse.data= SerializeJSON(LOCAL.inventoryevent);
           
               
           
} else {
               
                LOCAL
.AjaxResponse.success= false;
               
ArrayAppend(LOCAL.AjaxResponse.errors, LOCAL.modelName & ' ' & "no inventoryeventid specified" );
           
}
            renderWith
(  data=LOCAL.AjaxResponse , hideDebugInformation=true);
           
       
} else {
           
            param name
="params.page" type="integer" default="1";
           
           
/* params.orderby="upcid ASC"; */
           
           
params.orderby="id DESC";
            scaffold
("index", gtableDescription);
       
}
   
}

 
public struct function GetNewAjaxResponse( ) {

       
//    Define the local scope.
       
var LOCAL = {};

       
//    Create new API response
        LOCAL
.AjaxResponse = {};
        LOCAL
.AjaxResponse["success"] = true;
        LOCAL
.AjaxResponse["errors"] = [];
        LOCAL
.AjaxResponse["data"] = "";
        LOCAL
.AjaxResponse["token"] = "";
                           

       
//    Return the empty AjaxResponse object
       
return (LOCAL.AjaxResponse);
 
}


QUESTION : How do I modify my AJAX javascript code below to 
   A) USE the  "_hidden" field to specify the Authenticity Token in my ajax PUT?
   B) USE "PATCH" Method instead of "PUT"? 


function     post_DbEvent_Update(dbEvent_id, iNewCount, iNewTotalValue) {

    var params = { format:'json', id:dbEvent_id, itemcount:iNewCount, totalvalue: iNewTotalValue};
    var paramsStr = jQuery.param( params );
   
    alert(g.api_url+'/inventoryevents/' + dbEvent_id + '?'+ paramsStr );
   
    $.ajax({
        url: (g.api_url+'/inventoryevents/' + dbEvent_id + '?'+ paramsStr )    ,
        type: "PUT",
        dataType: "json",
        contentType: 'application/json',
        cache:false,
        data: {
id:dbEvent_id }
        timeout: 15000,
       
        success: function( rsp ) {
            alert( "post_DbEvent_Update() rsp is " + rsp );
           
            manageStatesAndTransitions(
                "json_rsp_db__ITEM"                /*  "event.type" */
                , rsp
                );   
        },

        error: function( xmlhttprequest, textStatusStr, errorThrownStr  ) {
            if(textStatusStr=="timeout") {
                alert("ERROR~723: getNext_DbItem() timed out");
            } else {
                alert( "Error~726 ${xmlhttprequest}   textStatus=" + textStatusStr + "  errorThrown=" + errorThrownStr);
            }           
        }
   
    });
}



QUESTION : How do I modify my CONTROLLER  code below to PROCESS the Authenticity Token in my controllers update() functin to prevent theWheels.InvalidAuthenticityToken error?


    /** * Update [inventoryevents] **/
   
function update()
   
{
        LOCAL
.mp =pluralize(this.modelName);
       
if (structKeyExists(params, "format") AND(params.format == "json"))
       
{
            LOCAL
.AjaxResponse = GetNewAjaxResponse();
           
if (structKeyExists(params, "totalvalue"))
           
{               
                   
if(structkeyexists(params, local.modelName)){
                   
                        variables
[local.modelName]=variables.model(local.modelName).findOneById(
                            value
=param.key);
       
                        variables
[local.modelName].update(params[local.modelName]);
       
                       
if ( variables[local.modelName].save() )  {
                            redirectTo
(route=local.mp, success=l(local.modelName) & ' ' & l("successfully updated"));
                       
}
                       
else {
                            renderView
(action="edit", error=l("There were problems updating that") & ' ' & l(local.modelName));
                       
}
                   
}  

                    LOCAL
.AjaxResponse.data =  SerializeJSON(LOCAL.inventoryevent);
                   
           
} // of structKeyExists(..."totalvalue"))
           
           
           
else { // NORMAL: return HTML page
           
                scaffold
("update", gtableDescription);
           
}
       
} // of json
   
}

QUESTION: Does Cfwheels 2.1 handle Authenticity Token better? should I upgrade from 2.0.2 to 2.1





Tom King

unread,
Jul 19, 2020, 3:54:43 AM7/19/20
to CFWheels
Ok, a fair few questions there... I've not played with CSRF for ages, so here goes:

QUESTION : How do I modify the index() function in my CONTROLLER  code below to return a  "_hidden" field containing a Authenticity Token in my ajax return structure?   
QUESTION : How do I modify my AJAX javascript code below to 
   A) USE the  "_hidden" field to specify the Authenticity Token in my ajax PUT? 
   B) USE "PATCH" Method instead of "PUT"? 

Instead of trying to get the CSRF code from the controller via Ajax, the usual method is to add the CSRF to the header meta tags.

You JS can then read the value from the meta tag and add it as a header for each ajax request
define(['jquery'], function ($) { var token = $('meta[name="csrf-token"]').attr('content'); $.ajaxSetup({ beforeSend: function (xhr) { xhr.setRequestHeader('X-CSRF-Token', token); } }); return token; });  

PATCH/PUT are interchangeable in wheels. 
So this should work just fine

let data = {"key":"value"} 
 $.ajax({ type: 'PUT', 
 contentType: 'application/json', 
 data: JSON.stringify(data), // access in body
})
.done(function () { console.log('SUCCESS'); })
.fail(function (msg) { console.log('FAIL'); })
.always(function (msg) { console.log('ALWAYS'); });  

QUESTION : How do I modify my CONTROLLER  code below to PROCESS the Authenticity Token in my controllers update() functin to prevent theWheels.InvalidAuthenticityToken error?  
If you've set it properly in the header as per the code above, you shouldn't need to do anything

QUESTION: Does Cfwheels 2.1 handle Authenticity Token better? should I upgrade from 2.0.2 to 2.1 
There's no noticeable change in CSRF from wheels 2.0.2->2.1 IIRC
But wheels 2.1 is better and more performant than wheels 2.0.2, so it's well worth the upgrade anyway (which should be relatively painless).
T
Reply all
Reply to author
Forward
0 new messages