I have an Yii application in which I have a link (outside a form) triggering an Angular controller posting some data. The problem is that Yii does not validate the CSRF token when that happens.
My raw url looks like this:
<a id="yt1" href="#" ng-click="markAllAsRead(23, '1eb4e3ac755e22939a0fc8d5ea0e9bacb453319a')" title="Read All" tooltips-size="small" tooltips="1" class="notification-tool ng-isolate-scope"><i class="fa fa-eye"></i></a>
My Angular controller calls an angular service which looks like this:
notificationsService.markAllAsRead = function (user_id, csrf) {
var dataObject = {
user_id: user_id,
YII_CSRF_TOKEN: csrf
};
$http.post("/api/notifications/readAll", dataObject).success(function (data) {
return data;
});
};
The POST request looks liek this:
If I disable the CSRF validation the call succeeds.
Any ideas?
Thanks!
COMPLETE ANSWER:
After some investigation I noticed that the $_POST
(also Yii's $request->getPost()
) was actually empty, even though Angular was posting data. Reading this answer on stackoverflow it seems that it actually is an issue that has to do with Angular JS and its' default behavior to post as apllication/json
(well, maybe not exactly an issue). As suggested by the marked answer to this question and based on the suggestion on the linked answer, I ended up overriding the CHttpRequest
class from Yii as follows:
class AppRequest extends CHttpRequest
{
public function validateCsrfToken($event)
{
if ($this->getIsPostRequest() ||
$this->getIsPutRequest() ||
$this->getIsPatchRequest() ||
$this->getIsDeleteRequest()
) {
$cookies = $this->getCookies();
$method = $this->getRequestType();
switch ($method) {
case 'POST':
if (empty($this->getPost($this->csrfTokenName))) {
$input = json_decode(file_get_contents('php://input'), true);;
$userToken = $input[$this->csrfTokenName];
} else {
$userToken = $this->getPost($this->csrfTokenName);
}
break;
case 'PUT':
if (empty($this->getPut($this->csrfTokenName))) {
$input = json_decode(file_get_contents('php://input'), true);;
$userToken = $input[$this->csrfTokenName];
} else {
$userToken = $this->getPut($this->csrfTokenName);
}
break;
case 'PATCH':
if (empty($this->getPatch($this->csrfTokenName))) {
$input = json_decode(file_get_contents('php://input'), true);;
$userToken = $input[$this->csrfTokenName];
} else {
$userToken = $this->getPatch($this->csrfTokenName);
}
break;
case 'DELETE':
if (empty($this->getDelete($this->csrfTokenName))) {
$input = json_decode(file_get_contents('php://input'), true);;
$userToken = $input[$this->csrfTokenName];
} else {
$userToken = $this->getDelete($this->csrfTokenName);
}
break;
}
if (!empty($userToken) && $cookies->contains($this->csrfTokenName)) {
$cookieToken = $cookies->itemAt($this->csrfTokenName)->value;
$valid = $cookieToken === $userToken;
} else
$valid = false;
if (!$valid)
throw new CHttpException(400, Yii::t('yii', 'The CSRF token could not be verified.'));
}
}
}