I am using the Synchronizer Token Pattern for standard forms (useToken = true) but I cannot find any recommended method of dealing with this over AJAX.
EDIT
Since posting this, I have rolled my own solution incorporating Grails existing pattern from above.
In the jQuery ajax I post the entire form (which will include Grails' injected SYNCHRONIZER_TOKEN and SYNCHRONIZER_URI hidden fields) such that the withForm closure can perform as expected in the controller.
The problem is, on successful response, there is no new token set (as the page is not reloaded and the g:form taglib is not evoked) and so I do this manually in the controller calling into the same library as the g:form taglib, and return it in the ajax response, and then reset the hidden field value. See below:
var formData = jQuery("form[name=userform]").serializeArray();
$.ajax({
type: 'POST',
url: 'delete',
data: formData,
success: function (data) {
// do stuff
},
complete: function (data) {
// Reset the token on complete
$("#SYNCHRONIZER_TOKEN").val(data.newToken);
}
})
in the Controller:
def delete(String selectedCommonName) {
def messages = [:]
withForm {
User user = User.findByName(name)
if (user) {
userService.delete(user)
messages.info = message(code: 'user.deleted.text')
} else {
messages.error = message(code: 'user.notdeleted.text')
}
}.invalidToken {
messages.error = message(code: 'no.duplicate.submissions')
}
// Set a new token for CSRF protection
messages.newToken = SynchronizerTokensHolder.store(session).generateToken(params.SYNCHRONIZER_URI)
render messages as JSON
}
Can anyone identify if I have unknowingly introduced a security flaw in the above solution. It looks adequate to me but I don't like hand rolling anything to do with security.
Nice!
IMO, you'd better reset the token at the same time.
SynchronizerTokensHolder.store(session).resetToken(params.SYNCHRONIZER_URI)
and if you have multiple forms in the same page, define a variable to hold tokens returned from each ajax request.
btw, why not implement the token pattern on your own?
UUID.randomUUID().toString()
, and store it into session with the url as the key.