1

I am trying to submit a simple form in UserFrosting and as a test only display the success message, with no data modification. I followed the guidance from Lesson 2 but I ran into the CSRF issue:

UserFrosting returns the following error:

Invalid or missing CSRF token.

What am I missing? Up until this point UserFrosting was very easy to digest :(

The form:

<form class="form-horizontal" role="form" name="requestDescription" action="{{site.uri.public}}/soap/requests/desc/edit/{{ keyname }}" method="post">
    <div class="form-group">
        <label for="input_group" class="col-md-2 control-label">Name</label>
        <div class="col-md-10">
            <input type="text" id="input_name" class="form-control" name="lgname" placeholder="{{ name }}">
        </div>
    </div>
    <div class="form-group text-center">
        <button type="submit" class="btn btn-success text-center">Update</button>
    </div>
</form>

with added script part to the bottom of the twig file:

<script>
    $(document).ready(function() {
        // Load the validator rules for this form
        var validators = {{validators | raw}};
        ufFormSubmit(
                $("form[name='requestDescription']"),
                validators,
                $("#userfrosting-alerts"),
                function(data, statusText, jqXHR) {
                    // Reload the page on success
                    window.location.reload(true);
                }
        );
    });
</script>

Here are my two functions from the controller:

public function soapRequestDescriptionEditPage($keyname){
    if (!$this->_app->user->checkAccess('uri_soap_requests')){
        $this->_app->notFound();
    }
    $requestDetails = $this->soapRequestReadMeta($keyname);

    $schema = new \Fortress\RequestSchema($this->_app->config('schema.path') . "/forms/soap-request-description-edit.json");
    $this->_app->jsValidator->setSchema($schema);

    $this->_app->render('soap/soap-request-description-edit.twig', [
        "name" => $requestDetails['name'],
        "description" => $requestDetails['description'],
        "keyname" => $keyname,
        "validators" => $this->_app->jsValidator->rules()
    ]);
}

public function test(){
    if (!$this->_app->user->checkAccess('uri_soap_requests')) {
        $this->_app->notFound();
    }
    $post = $this->_app->request->post();
    $ms = $this->_app->alerts;
    $requestSchema = new \Fortress\RequestSchema($this->_app->config('schema.path') . "/forms/soap-request-description-edit.json");
    $rf = new \Fortress\HTTPRequestFortress($ms, $requestSchema, $post);
    $ms->addMessageTranslated("success", "Everyone's title has been updated!", $post);
    $rf->sanitize();
    if (!$rf->validate()) {
        $this->_app->halt(400);
    }
    $data = $rf->data();
}   

Entries from the index.php file:

$app->post('/soap/requests/desc/edit/:request_id/?', function () use ($app) {
    $controller = new UF\SoapController($app);
    return $controller->test();
});
$app->get('/soap/requests/desc/edit/:request_id/?', function ($request_id) use ($app) {
    $controller = new UF\SoapController($app);
    return $controller->soapRequestDescriptionEditPage($request_id);
});

Finally, the schema:

{
  "lgname" : {
    "validators" : {
      "length" : {
        "min" : 1,
        "max" : 150,
        "message" : "The new title must be between 1 and 150 characters long."
      }
    },
    "sanitizers" : {
      "raw" : ""
    }
  }
}
Luke G
  • 1,741
  • 6
  • 23
  • 34
  • You need to send CSRF token in every request you can find what is CSRF here https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) – MONTYHS Nov 21 '16 at 12:14
  • 1
    See https://github.com/userfrosting/UserFrosting/wiki/On-the-matter-of-CSRF-tokens for more information on this error. Also, I'm not sure if the "soap" in your urls and controllers refers to SOAP, but UserFrosting uses a mostly RESTful approach, which is at odds with SOAP. – alexw Nov 22 '16 at 16:06

2 Answers2

1

As of UserFrosting 4, you should explicitly add the hidden CSRF input fields to your form. There is a partial template forms/csrf.html.twig that contains these fields, which you can insert using Twig's include tag:

<form class="form-horizontal" role="form" name="requestDescription" action="{{site.uri.public}}/soap/requests/desc/edit/{{ keyname }}" method="post">
    {% include "forms/csrf.html.twig" %}
    <div class="form-group">
        <label for="input_group" class="col-md-2 control-label">Name</label>
        <div class="col-md-10">
            <input type="text" id="input_name" class="form-control" name="lgname" placeholder="{{ name }}">
        </div>
    </div>
    <div class="form-group text-center">
        <button type="submit" class="btn btn-success text-center">Update</button>
    </div>
</form>

For requests that are made without a form (for example, if it has been constructed purely in Javascript), you can grab the CSRF token name and value from the global site.csrf variable:

var userName = 'luke';
var fieldName = 'lgname';

var data = {
    'value': fieldValue
};

data[site.csrf.keys.name] = site.csrf.name;
data[site.csrf.keys.value] = site.csrf.value;

var url = site.uri.public + '/api/users/u/' + userName + '/' + fieldName;

return $.ajax({
    type: "PUT",
    url: url,
    data: data
}).done(function (response) {
    window.location.reload();
});
alexw
  • 8,468
  • 6
  • 54
  • 86
0

It turned out that my code was fine. There were unrelated javascript errors on the page affecting UserFrosting form processing. Fixing these errors allowed UserFrosting to handle the form.

Note to self... make it a habit to look into the console for javascript errors :)

Luke G
  • 1,741
  • 6
  • 23
  • 34