Her I have created login form and with csrf token If i send first request it will validate properly. Now I tried with wrong passwod than new csrf token is received from server. And When Again i will resend request with correct password it will show error 403.
Here is my code. 1 . View File
<form action="<?=$module."/validate_login"?>" id="form" name="form">
<input type="hidden" class="txt_csrfname" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />
<div class="mb-4">
<label class="mb-1 text-dark">Email</label>
<input type="email" class="form-control form-control" id="username" name="username"
placeholder="hello@example.com">
<div class="invalid-feedback"></div>
</div>
<div class="mb-4 position-relative">
<label class="mb-1 text-dark">Password</label>
<input type="password" id="dz-password" name="password" class="form-control"
placeholder="Password">
<span class="show-pass eye">
<i class="fa fa-eye-slash"></i>
<i class="fa fa-eye"></i>
</span>
<div class="invalid-feedback"></div>
</div>
<div class="form-row d-flex justify-content-between mt-4 mb-2">
<div class="mb-4">
<div class="form-check custom-checkbox mb-3">
<input type="checkbox" class="form-check-input" id="customCheckBox1">
<label class="form-check-label" for="customCheckBox1">Remember my
preference</label>
</div>
</div>
<div class="mb-4">
<a href="<?=base_url("forgot-password")?>" class="btn-link text-primary">Forgot
Password?</a>
</div>
</div>
<div class="text-center mb-4">
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</div>
<div class="form-row" id="alert-message" style="display: none;">
<div class="alert alert-danger alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert"
aria-label="btn-close"><span><i class="fa-solid fa-xmark"></i></span>
</button>
<strong>Error!</strong> In-valid username or password.
</div>
</div>
</form>
2 . JS FILE
$("#form").validate({
rules : {
username : 'required',
password : 'required'
},
messages : {
username : 'Please enter e-mail',
password : 'Please enter password'
},
submitHandler : function(form,e){
e.preventDefault();
var form_data = new FormData(form);
$.ajax({
url : SITE_URL+"validate-login",
type : "POST",
contentType: false,
cache: false,
processData:false,
data : form_data,
beforeSend : function(){
},
success : function(response){
$('.txt_csrfname').val(response.data.token);
if(response.success == "1"){
$("#alert-message").css("display","none");
window.location.href= SITE_URL+"Dashboard";
}else{
console.log(response.data.token);
$("#alert-message").css("display","");
}
},
error : function(){
}
});
}
});
Controller
public function validate_login(){
$request = service('request');
$requestArr = $request->getPost();
$responseArr = array("success"=>"1","data"=>array(),"message"=>"");
$data = array();
$data['token'] = csrf_hash();
$ipaddress = $this->request->getIPAddress();
$rules = [
'username' => 'required|valid_email',
'password' => 'required'
];
if($this->validate($rules)){
$userArr = $this->Auth->login($requestArr['username']);
if(!empty($userArr)) {
$verify_password = password_verify($requestArr['password'],$userArr->password);
if($verify_password){
$responseArr['success'] = "1";
$responseArr['message'] = "Loged in successfully.";
$this->session->set([
'tbl_users_id'=>$userArr->tbl_users_id,
'full_name'=>$userArr->full_name,
'username'=>$userArr->username,
'email'=>$userArr->email
]);
$logArr = array(
'user_id' => $userArr->tbl_users_id,
'module' => $this->module,
'module_id' => $userArr->tbl_users_id,
'action' => "LOGIN",
'ip_address' => $ipaddress,
'remarks' => "User Loged In",
'raw_data'=> ""
);
insert_log($logArr);
}else{
$responseArr['success'] = "0";
$responseArr['message'] = "In-valid username or password.";
}
}else{
$responseArr['success'] = "0";
$responseArr['message'] = "In-valid username or password.";
}
}else{
$responseArr['message']= "Required Fields are missing";
}
$responseArr['data']= $data;
output($responseArr);
}
It will validate CSRF Token only first call. After that it will show 403 error. Want to validate csrf token with all ajax request.
Well, it's most likely that you've set \app\Config\Security::$regenerate
to true
.
\app\Config\Security
/**
* --------------------------------------------------------------------------
* CSRF Regenerate
* --------------------------------------------------------------------------
*
* Regenerate CSRF Token on every submission.
*/
public bool $regenerate = true;
This setting would regenerate the token for every ['POST', 'PUT', 'DELETE', 'PATCH']
HTTP request.
In addition, in your Javascript source code, you tend to update the token: $('.txt_csrfname').val(response.data.token);
in your AJAX success handler yet your Controller method validate_login()
doesn't provide/send such information. Another issue I would raise is, what if it's not successful? Your AJAX error handler is empty, resulting in a token mismatch on the next POST HTTP request since you didn't update your front-end CSRF token on error.
Disable \app\Config\Security::$regenerate
.
public bool $regenerate = false;
Then, you won't have to update your CSRF token on every POST
HTTP request ($('.txt_csrfname').val(response.data.token);
).
Enable \app\Config\Security::$regenerate
and design an efficient mechanism to update your front-end CSRF token on every ['POST', 'PUT', 'DELETE', 'PATCH']
HTTP request. And the front-end CSRF token update must happen whether the request is successful or not.
I provided such a solution in a question I answered previously. codeigniter4 code to add employee redirects to itself on submit when regenerate is set to true in config.security