Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing scala Play (2.2.1) controllers with CSRF protection

I've been having some problems testing controllers that use Play's CSRF protection. To demonstrate this, I've created a very simple Play application that minimally exhibits the problem.

https://github.com/adamnfish/csrftest

The full details are on the README of that repository, but to summarise here:

Consider a controller that is designed to handle a form submission. It has a GET method that uses CSRFAddToken and a POST method that uses CSRFCheck. The former adds a CSRF Token to the request so that a form field can be put in the rendered view, containing the valid token. When that form is submitted, if the CSRF check passes and the submission is valid, something else will happen (typically a redirect). If the form submission is not valid, the form submission is re-shown along with any errors so the user can correct the form and submit again.

This works great!

However, in the tests we now have some problems. To test the controller you can pass a fake request to it in the test. The CSRF check itself can be skipped by adding the nocheck header to the fake request but the view cannot be rendered because no token available to generate the form field. The test fails with a RuntimeException, "Missing CSRF Token (csrf.scala:51)".

Given that it works when it's actually running but not in the tests, it seems like this must be a problem with the way FakeRequests are run in Play tests but I may be doing something wrong. I've implemented the CSRF protection as described at http://www.playframework.com/documentation/2.2.1/ScalaCsrf and the testing as described at http://www.playframework.com/documentation/2.2.1/ScalaFunctionalTest. I'd appreciate any pointers if anyone has managed to test CSRF protected forms.

like image 780
adamnfish Avatar asked Dec 15 '22 04:12

adamnfish


1 Answers

One solution is to test using a browser, eg Fluentlenium, as this will manage cookies etc, so the CSRF protection should all just work.

The other solution is to add a session to the FakeRequest so that it contains a token, eg:

FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken)

Obviously if you're doing that a lot, you can create a help method to do that for you.

like image 56
James Roper Avatar answered Jan 05 '23 00:01

James Roper