Having sorted out the CORS issue and succesfully logging into Craft from a different domain using Javascript, I've now turned CSRF protection back on. I've got a module returning the CSRF token name and value fine. I fetch the CSRF token with a GET request, then use that token in my login form submission. But I cannot get past Craft being unable to verify the data submission. The same code works fine when run from the same domain.
Does anyone have any experience of doing this?
function fetchCSRFToken() {
var url = 'http://server.localhost/actions/server-module/server/get-csrf';
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
return fetch(url)
.then(status)
.then(json)
.then(function(tokenData) {
return tokenData;
}).catch(function(error) {
});
}
function login(event, tokenData) {
var rawAction = "http://server.localhost/" + event.target.action.value;
var action = encodeURIComponent(event.target.action.value);
var csrfTokenName = encodeURIComponent(tokenData.name);
var csrfTokenValue = encodeURIComponent(tokenData.value);
var loginName = encodeURIComponent(event.target.loginName.value);
var password = encodeURIComponent(event.target.password.value);
var data = "action="+action+"&"+csrfTokenName+"="+csrfTokenValue+"&loginName="+loginName+"&password="+password;
var ajax=new XMLHttpRequest();
ajax.onreadystatechange = function(){
if(this.readyState == 4 && this.status ==200){
console.log(this.responseText);
}
}
ajax.open('POST', rawAction, true);
ajax.setRequestHeader('Accept', 'application/json, text/javascript, */*; q=0.01');
ajax.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
ajax.send(data);
}
document.querySelector('#loginform-js').addEventListener('submit', function(event) {
event.preventDefault();
fetchCSRFToken().then(function(token) {
login(event, token);
});
});
And how I get the CSRF token from a module controller:
/**
* @return Response
*/
public function actionGetCsrf(): Response
{
return $this->asJson([
'name' => Craft::$app->getConfig()->getGeneral()->csrfTokenName,
'value' => Craft::$app->getRequest()->getCsrfToken(),
]);
}
EDIT: I suspect the preflight CORS request is invalidating the CSRF token? The preflight is only necessary on the remote server.