The reason why this code does not work:
$csrf = new Hidden('csrf', array( 'value' => $this->security->getToken(),));
$csrf->addValidator(
new Identical(array(
'value' => $this->security->getSessionToken(),
'message' => 'CSRF validation failed'
)));
$this->add($csrf);
is that when you execute $this->security->getToken() a NEW value is generated and saved in the session, so the $this->security->getSessionToken() will return the new one and not the one generated on the previous page, where you submitted the form.
On the other hand the
html{{ form.render('csrf', ['value': security.getToken()]) }}
works because the template's code is run after the form's code so no new value is generated. But works only when you use this method once/page.
So my workaround is:
protected static $csrf;
protected function addCsrf(){
if(!self::$csrf) {
self::$csrf = array(
'old'=> $this->security->getSessionToken(),
'new'=> $this->security->getToken(),
);
}
$field = new Hidden('csrf', array('value'=> self::$csrf['new']));
$field->addValidator(
new Identical(array(
'value' => self::$csrf['old'],
'message' => 'Refresh detected. CSRF Verification Failed'
))
);
$this->add($field);
}
</code>