1

When calling a url with a 'Get' request, I'm able to get a response just fine.

However, when I'm calling the same url with a 'Post' request, I get a 'bad request'. Any suggestions/ideas on why this may be happening?

Code in the index.php file:

//GET Request: This works fine!

$app->get('/api/ss20/registration/?', function () use ($app) {
            echo "hello-get";
});


//POST Request: This gives a bad request error!

$app->post('/api/ss20/registration/?', function () use ($app) {
            echo "hello-post";
});

The simple form I'm using to make the get request:

<form action="api/ss20/registration/" method="get">
  First name: <input type="text" name="fname"><br>
  Last name: <input type="text" name="lname"><br>
  <input type="submit" value="Submit">
</form>

The simple form I'm using to make the post request:

<form action="api/ss20/registration/" method="post">
  First name: <input type="text" name="fname"><br>
  Last name: <input type="text" name="lname"><br>
  <input type="submit" value="Submit">
</form>

Tried using postman / Arc to simulate the requests. And the get requests work fine, but not posts.

accord_guy
  • 314
  • 2
  • 11

2 Answers2

2

You're most likely getting a 400 bad request error because of a missing CSRF token.

In version 0.3.1, UserFrosting stores the CSRF token in a special <meta> tag inside the page's <head> tag. For example, if I look at the source for the account/settings page, I'll see this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Update your account settings, including email, display name, and password.">
    <meta name="author" content="Alex Weissman">
    <meta name="csrf_token" content="583caa307d5c1027a8983251f13a3fe994050001d871e329b16599a29858652891a10c48535c6e4be0580c87503f210072ca83c0a45d49c0d0941b5b2536aa15"> 

    <title>UserFrosting | Account Settings</title>
    ...

So somehow, you need to get that value for csrf_token into your POST submission. Normally, UserFrosting will do this automatically if you submit your form with Javascript using the ufFormSubmit function.

You'll notice these lines:

// Append page CSRF token
var csrf_token = $("meta[name=csrf_token]").attr("content");
serializedData += "&csrf_token=" + encodeURIComponent(csrf_token); 

These simply grab the csrf_token value from the <meta> tag and append it to your form's data. Note that for this function to be called, you need to make sure that you don't have any Javascript errors on your page. Otherwise, most browsers will quietly default to a "traditional" form submission, and the CSRF token won't get appended to your form.

If you really do want to submit your form the "traditional" way, you can insert the CSRF token directly into your Twig template as a hidden field. Simply add

<input type="hidden" name="{{csrf_key}}" value="{{csrf_token}}">

to your form. More about UserFrosting and CSRF tokens here.

alexw
  • 8,468
  • 6
  • 54
  • 86
  • The API call is being made by another service though. Can the call be made without the CSRF token? – accord_guy Jul 25 '16 at 08:09
  • 1
    In that case, you'll have to whitelist the route(s) that process requests from that service. To do this in UF 0.3.1, you'll have to modify the [`CsrfGuard`](https://github.com/userfrosting/UserFrosting/blob/master/userfrosting/middleware/CsrfGuard.php#L97-L104) class. – alexw Jul 25 '16 at 19:47
  • Thanks @alexw. That did it! – accord_guy Jul 26 '16 at 17:38
1

I needed to modify middleware/CsrfGuard.php to whitelist the API url

Added the following if statement. It checks that POST requests to /api/ss20/registration are ignored.

if (!(in_array($this->app->request()->getMethod(), array('POST')) && $uri->getPath() == "/api/ss20/registration"))
        {
            if (in_array($this->app->request()->getMethod(), array('POST', 'PUT', 'DELETE')))  {

                 //error_log("\n".date(DATE_RSS)." Inside CSRF Validation",3, "/tmp/php-error.log");

                $userToken = $this->app->request()->post($this->key);
                if ($token !== $userToken) {
                    //error_log("\n".date(DATE_RSS)." About to send halt-400",3, "/tmp/php-error.log");
                    $this->app->alerts->addMessage('danger', 'Invalid or missing CSRF token.');
                    $this->app->halt(400);
                }
            }
}

Thanks @alexw

accord_guy
  • 314
  • 2
  • 11