Closed nireve closed 9 years ago
It turns out it has to with how angular-ui-router caches the ajax content. So if I send the new CSRF token with content A, delete request is successful. Then content B is loaded (newer token generated), going back to content A then delete will return 400 because A is cached and still has the old token.
A possible solution is to create an Application Component, and filter all outgoing response and insert javascript to re-assign meta fields with the latest token.
class CSRFReassign extends Component {
public function init() {
Event::on(Response::className(), Response::EVENT_AFTER_PREPARE, function ($event) {
$response = $event->sender;
$csrf = \Yii::$app->request->csrfToken;
// only when content is html are csrf tokens re-issued
if ($response->format == Response::FORMAT_HTML) {
$response->content .= "<script>$('meta[name=csrf-token]').prop('content', '$csrf');</script>";
}
});
}
}
Enable this component in the config file:
'bootstrap' => ['log', 'CSRFReassign'],
'components' => [
'CSRFReassign' => [
'class' => '[your namespace]\CSRFReassign'
]
...
]
Then anytime we make the request, use window.yii.getCsrfToken()
to retrieve the (latest) token as usual, no matter how many ajax requests have been made.
In my AngularJS app I use ui-router to load partial page content that contains a GridView with delete button. The content returned are pure html without inline or linked javascript.
With vanilla GridView, the delete button will actually submit a form with a _csrf parameter whose value is retrieved via
yii.getCsrfToken()
. I rewrote it so that it now submits delete request via AJAX by using angular's $http to make it look like form submission, including sending the correct content-type header and form data. Two approaches were used to retrieve CSRF tokens, neither of which works completely:yii.getCsrfToken()
in the form data will yield "400 Bad Request. Unable to verify your data submission." most of the times, but once in a while it will accept the request; OR<?= Yii::$app->request->csrfToken ?>
somewhere to include the latest token, and set that to _csrf in the form data, and it works sometimes, and gives 400 response other times.What exactly could go wrong here? I'm using DbSession if that helps. Thanks in advance!
UPDATE: It turns out it has to with how angular-ui-router caches the ajax content. So if I send the new CSRF token with content A, delete request is successful. Then content B is loaded (newer token generated), going back to content A then delete will return 400 because A is cached and still has the old token. See comment for one way of solving this issue.