I got strange issue. I'm implementing cart functionality on my website and I use session to store cart positions. I have a POST action to add new position to cart, and I have CSRF filter enabled to secure website. I call it with ajax on a product page, so first call is okay, but second says Unauthorized and in logs there are [CSRF] Check failed because no token found in headers for /cart
. But it has. I call it with:
$("form").submit(function(e){
e.preventDefault();
$.ajax({
url: '/cart',
method: 'POST',
data: getCartPosition(),
beforeSend: function(xhr){xhr.setRequestHeader('Csrf-Token', $('input[name=csrfToken]').val());},
success: function (data, textStatus) {
alert('Added!');
},
error: function (error) {
alert('Error!');
}
});
});
and I put CSRF token in template somewhere:
@CSRF.formField
and it's in request:
I have enabled this in config
play.filters.csrf.bypassCorsTrustedOrigins=true
play.filters.hosts {
# Allow requests to example.com, its subdomains, and localhost:9000
allowed = ["localhost:9000", "localhost:4200"]
}
But what is strange that it seems it puts csrfToken in session, because after failed request I have session like this
Session(Map(cart -> {"positions":
[{"trackId":1},{"trackId":24},{"trackId":20}]},
username -> user,
token -> 0639d0b0-e7c8-4e82-9aad-2a43044e72db,
csrfToken -> e705413843ea96a6491a0e9e800ba36a712c4f70-1506542471068-0baeef7535eb9c889fb6fed2))
Idk why it's there, my add2cart action looks like:
private def cartAction(addToCartForm: Form[CartPosition], action: (Cart, CartPosition) => Cart)(implicit request: UserRequest[Any]) = {
addToCartForm.fold(
_ => BadRequest("Error!"),
position => {
getCart match {
case Some(cart) => Ok("Ok").withSession("cart" -> Json.toJson(action(cart, position)).toString(), "username" -> request.session.get("username").getOrElse(""), "token" -> request.session.get("token").getOrElse(""))
case _ => Ok("Ok, no").withSession("cart" -> Json.toJson(action(Cart(Seq.empty), position)).toString())
}
}
)
}
def addToCart() = guestAction { implicit request =>
cartAction(addToCartForm.bindFromRequest, addCartPos)
}
and addCartPos just adds position to json
I've got same issue with Play 2.7.3.
In my case, the form is generated by the Twirl with the csrf token
and because I'm using ajax to submit the form, I've copied the csrf token
from the rendered form and pass it to the ajax header as writen in the Play's documentation.
The form can be submitted multiple times so I need to updated the token. Therefore I'm passing through ajax response new csrf token
taken in the controller from play.filters.csrf.CSRF.getToken.get.value
.
But unfortunatelly, the second submission failed as cutoffurmind mentioned.
And the fix is as described by Knut Arne Vedaa to add new token to the session. I did it by the withSession
method.
So the controller response is looking like this:
Ok(Json.obj(
"status" -> (user != None),
"notif" -> "Success login",
"data" -> Map(
"adminUrl" -> "www.something ...",
"csrf" -> play.filters.csrf.CSRF.getToken.get.value
)
)).withSession(
"uid" -> user.getOrElse(User()).id.toString,
"csrfToken" -> play.filters.csrf.CSRF.getToken.get.value
)
It doesn't look like an issue as Play Framework doesn't have session data kept on the server, so it is logical that the token has to be updated in the client site after the ajax request. The main issue is that it is not mentioned in the documentation (in the CSRF ajax section), what could be handy as people simply doesn't read the doc from A to Z in expected order.
In my case the solution was to set the play.filters.csrf.cookie.name
config option to a value other than null:
play.filters.csrf.cookie.name = csrf_token
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With