How to include CSRF from Codeigniter into ajax data - codeigniter

I am trying to pass some data into my Controller, but I'm getting a 500 error. After some research, I discovered that it's caused by the CSRF token not being sent.
It seems like I need to include this along with the data: <?php echo $this->security->get_csrf_token_name(); ?>:"<?php echo $this->security->get_csrf_hash(); ?>"
My JS is pretty weak, so I'm a little confused on how to alter this to include the above.
<script type="text/javascript">
$(document).ready(function() {
$("#order").sortable({
update : function (event, ui) {
order = $('#order').sortable('serialize');
$.ajax({
url: "<?=base_url().'admin/category/update_order'?>",
type: "POST",
data: order,
success: function(response){
console.log(response);
}
});
}
});
}
);
</script>

The token needs to be passed in the data argument of $.ajax.
This should work but see my notes below.
order['<?php echo $this->security->get_csrf_token_name(); ?>'] = '<?php echo $this->security->get_csrf_hash(); ?>';
However, there are a few bad practices going on here. Mainly you should not use PHP in your javascript because this prevents you from being able to access the javascript as a separate file (this is good because browsers will cache it to make your page load faster and consume less bandwidth).
It's better to store the token in your order <form> html like this..
<input type="hidden" name="<?php echo $this->security->get_csrf_token_name(); ?>" value="<?php echo $this->security->get_csrf_hash(); ?>" />
Then it will get serialized with the rest of your form data.
You can also store the URL in the form's action attribute. This will help your script gracefully degrade and also keeps the URL in one place instead of 2.
<form id="order" method="post" action="<?=base_url()?>admin/category/update_order">
In the $.ajax call, use something like this url: $('#order').attr('action'), assuming #order is the actual form id.

CI stores csrf in cookie and you can fetch it from there:
var csrf = $.cookie('csrf_cookie_name');
The downside of this method is that jQuery doesn't natively provide cookie access. So you will need a jquery plugin.

You're correct, just add the CSRF token to your post data. You can use jQuery's $.extend function to merge your order object you already created with the CSRF token data, like this:
$.extend(alerts, {
'<?php echo $this->security->get_csrf_token_name(); ?>' :
'<?php echo $this->security->get_csrf_hash(); ?>' });

Here's a different approach. Simple function in Auth.php that returns the csrf token name and hash in JSON format. Then, in our javascript, make two ajax calls, the first to grab the csrf creds and insert them into hidden form fields, the second to handle our actual form submit.
// place function in Auth.php controller
public function get_csrf()
{
$csrf['csrf_name'] = $this->security->get_csrf_token_name();
$csrf['csrf_token'] = $this->security->get_csrf_hash();
echo json_encode($csrf);
}
// myFunction()
<script type="text/javascript">
function myFunction() {
// first ajax call to grab the csrf name and token
// from our get_csrf() function in Auth.php
$.ajax({
type: "GET",
dataType: 'json',
url: "https://<your_domain>.com/auth/get_csrf", //replace with your domain
success: function (data) {
// assign csrf name and token to variables
csrf_name = data.csrf_name;
csrf_token = data.csrf_token;
// assign field1 and field2 field values to variables
var form_field1 = document.getElementById('field1').value;
var form_field2 = document.getElementById('field2').value;
// insert csrf creds into form
$('#csrftoken').attr('name', csrf_name);
$('#csrftoken').val(csrf_token);
// second ajax call -- this is our form handler
$.ajax({
type: "POST",
url: "https://<your_domain>.com/<your_controller>/<your_function>", //replace with your domain/controller/function
data: {
// ES6 computed property keys are great...
[csrf_name] : csrf_token,
"field1" : form_field1,
"field2" : form_field2
},
success: function(data) {
// handle the return from your form submit
if (data == "ok") {
console.log("Success!");
$("#infoMessage").html("<p>Success</p>").fadeIn(300);
} else {
console.log("Oops!");
$("#infoMessage").html("<p>Oops!</p>");
}
}
});
}
});
}
</script>
// html
<div id="infoMessage"></div>
<form method="post" accept-charset="utf-8">
<input type="hidden" id="csrftoken" name="" value="" />
<label for="field1">Field 1:</label> <br />
<input type="text" name="field1" id="field1" />
<label for="field2">Field 2:</label> <br />
<input type="text" name="field2" id="field2" />
<input type="button" name="submit" value="SUBMIT" onclick="myFunction()" />
</form>

I extend form_helper as MY_form_helper.php to serve csrf tokens to my own form generation code - you could use something similar:
function secure_form() {
$CI =& get_instance();
return '<input type="hidden" name='.$CI->security->get_csrf_token_name().' value='.$CI->security->get_csrf_hash().'>';
}

another solution is to use .serialize():
$.post("user/save", $('#frmUser').serialize(),function(html){
$("#results").html(html);
});
that will find the hidden field that stores the CSRF data.

Related

After form submit redirect without refresh using Ajax in laravel 8

I am developing multi Step Form Submit without refresh. collect the data from 1st step 2nd step collect some date, 3rd step collect some date & finally submit data in the database. Can you tell me how to fix this.
My blade template.
<form id="post-form" method="post" action="javascript:void(0)">
#csrf
<div>
<input class="form-input" type="text" id="ptitle" name="ptitle" required="required"
placeholder="What do you want to achieve?">
</div>
<button type="text" id="send_form" class="btn-continue">Continue</button>
</div>
</form>
Ajax Script
$(document).ready(function() {
$("#send_form").click(function(e){
e.preventDefault();
var _token = $("input[name='_token']").val();
var ptitle = $('#ptitle').val();
$.ajax({
url: "{{route('create.setp2') }}",
method:'POST',
data: {_token:_token,ptitle:ptitle},
success: function(data) {
alert('data.success');
}
});
});
Web.php router
Route::post('/setp2', [Abedoncontroller::class, 'funcsetp1'])->name('create.setp2');
Controller method
public function funcsetp1(Request $request) {
$postdata=$request->input('ptitle');
return response()->json('themes.abedon.pages.create-step-2');
}

Success function not being called after making AJAX request codeigniter

When I make an AJAX call from view and pass form data to the controller. I get a couple of problems. First, the code inside success is never executed, and second, the page is being refreshed even though it is an AJAX call. Can anyone tell me where am I doing wrong?
I have seen a lot of questions since yesterday but none of them were able to solve my problem.
Model code
public function insert_user($name, $email) {
$data = array();
$data['name'] = $name;
$data['email'] = $email;
$data['created_at'] = date('y-m-d');
$this->db->insert('all_users', $data);
return true;
}
Controller code
public function insert_user () {
$data = $this->input->post();
$name = $data['name'];
$email = $data['email'];
$this->User_model->insert_user($name, $email);
$this->load->view('view');
}
Ajax request code
const insertBtn = $(".insert-btn");
insertBtn.on("click", function () {
const name = $(".insert-form input[type=name]");
const email = $(".insert-form input[type=email]");
$.ajax({
url: "<?php echo base_url() ?>index.php/Users/insert_user",
type: "post",
data: {name, email},
dataType: "json",
success: function () {
$("body").append("Request made successfully");
}
})
});
My form looks something like this:
<form class="insert-form" action="<?php echo base_url() ?>index.php/Users/insert_user" method="post">
<input type="text" name="name" placeholder="Enter name">
<input type="email" name="email" placeholder="Enter email">
<button class="insert-btn">Insert Data</button>
</form>
NOTE: I am able to successfully insert data into the database.
The browser is submitting the form before your AJAX code gets a chance to run/finish.
Instead of binding an event to the click event of the button, you want to bind to the submit event of the form. Then you want to cancel the browser's default action. This is done via the e.preventDefault(); method.
Also, dataType: "json" is not needed here. dataType tells jQuery what kind of data your AJAX call is returning. You generally don't need it as jQuery can automatically detect it. Plus, if you are not returning a JSON document, then this may cause a problem.
const insertForm = $(".insert-form");
insertForm.on("submit", function (e) {
const name = insertForm.find("input[type=name]");
const email = insertForm.find("input[type=email]");
e.preventDefault();
$.ajax({
url: "<?php echo base_url() ?>index.php/Users/insert_user",
type: "post",
data: {name, email},
success: function () {
$("body").append("Request made successfully");
}
})
});
Controller code
public function insert_user () {
$data = $this->input->post();
$name = $data['name'];
$email = $data['email'];
$data = $this->User_model->insert_user($name, $email);
$this->output
->set_content_type('application/json')
->set_output(json_encode($data));
}
Ajax request code
const insertBtn = $(".insert-btn");
insertBtn.on("click", function () {
const name = $(".insert-form input[type=name]");
const email = $(".insert-form input[type=email]");
$.ajax({
url: "<?php echo base_url() ?>Users/insert_user", // <?php echo base_url() ?>controller_name/function_name
type: "post",
data: {name, email},
dataType: "json",
success: function () {
$("body").append("Request made successfully");
}
})
});
form looks something like this:
<form class="insert-form" method="post">
<input type="text" name="name" placeholder="Enter name">
<input type="email" name="email" placeholder="Enter email">
<button class="insert-btn">Insert Data</button>
</form>
The page was being refreshed because I had a button that was acting as submit button on changing it to the input of the type button it does not submits the form and we don't see the page being refreshed. And also the AJAX request made also runs successfully.
<form class="insert-form" action="<?php echo base_url() ?>index.php/Users/insert_user" method="post">
<input type="text" name="name" placeholder="Enter name">
<input type="email" name="email" placeholder="Enter email">
<input type="button" class="insert-btn" value="Insert Data">
</form>

how to fix occasional return data when uploading file with ajax

I have a problem with my code. I created upload file with ajax in Laravel. I am using tutorial from here, and yes it is working. But, it working sometimes.
I have no idea where the problem is. But I have changing max_execution_time in php.ini into 0 value. I also included <code>csrf_token()</code> in ajax headers
I wish there somebody help me with this code. I don't know, but the code looks good for me. Here is the code
blade view
<form class="form-main" enctype="multipart/form-data" method="post" id="formbank" >
<div class="field file" data-title="Upload File">
<input type="file" id="myfile" name="myfile" accept="image/*">
<label for="myfile"><i class="fa fa-upload"></i> <span id="file-title">Select File</span></label>
</div>
<div class="field" data-title="Action">
<button class="but-main">Submit</button>
</div>
</form>
JS script
$(document).ready(function(e){
$('#formbank').on('submit',function(e){
e.preventDefault();
var fd = new FormData(this);
fd.append('myfile',$('#myfile')[0].files[0]);
$.ajax({
async: true,
type:'post',
data:fd,
contentType: false,
cache:false,
processData: false,
headers: {
'X-CSRF-TOKEN' : "{{ csrf_token() }}"
},
url: "{{ route('post.bank') }}",
success:function(data){
console.log(data);
},
error:function(data){
console.log(data);
}
});
});
});
post.bank controller
public function createBank(Request $request){
if ($request->hasFile('myfile')) {
$file = $request->file('myfile');
return $file->getClientOriginalName();
}
else {
$text = 'empty';
return $text;
}
In this case, I only try to return name of uploaded file. Here is the result.
https://drive.google.com/file/d/1zK5YmO8f8cGR110X-oi2bTVMiaMCXYi9/view?usp=sharing "result"
Thank you for all suggestion. Many thanks. After several trial and errors, I find a trick to resolve it by calling main function with setTimeOut(). And i put _token validation in controller. When token is empty, it will return a value that let setTimeOut() do the rest of time until token is loaded correctly.
I guess that way is suited for me. And I will be happy if there is a better answer to resolve it in a better way. Thank you anyway..

Need e.preventDefault to save JWT into localStorage

For some reason, when trying to login I need to have e.preventDefault (prevent page reloading) in order to save my JWT into local storage with an AJAX call. So when i have this:
handleLogin(e) {
//Without e.preventDefault, the jwt token is not save -> cannot access api
e.preventDefault();
const email = $('#email').val()
const password = $('#password').val()
const request = {"auth": {
"email": email,
"password": password
}}
$.ajax({
url: "http://localhost:5000/api/user_token",
type: "POST",
data: request,
dataType: "json",
success: function (result){
console.log(result.jwt)
localStorage.setItem("jwt", result.jwt)
}
})
}
Here is my simple form
render(){
return(
<div>
<form>
<input
name="email"
id="email"
type="email"
/>
<input
name="password"
id="password"
type="password"
/>
<button
onClick={this.handleLogin}
>Login</button>
<button
onClick={this.handleLogout}
>Logout</button>
</form>
<button onClick={this.getUsers}>Get Users</button>
{
this.state.users
}
</div>
)
}
I want my page to reload/go to a different page after submitting a successful login. On create-react-app and using a Rails API 5
In your case, you can try this:
success: function (result){
console.log(result.jwt)
localStorage.setItem("jwt", result.jwt)
//page reload
window.location.reload(true);
// or route to another page
window.location.href = 'foo'; // any route
}
But I would recommend to use react router so your app will never loose it's state.
If you have any query, you can ask.

ajax request to controller to update view in laravel

I can't find a working solution for this problem:
I want to update a part of my view without reloading it,
I have a form that collects the data to be passed to the controller,
the controller needs to get the data from the DB and spit out a JSON
to the view so that it can be filled with such data.
I tried to adapt this http://tutsnare.com/post-data-using-ajax-in-laravel-5/ but it's not working at all. The data collected is not reaching the controller.
My uderstanding is the javascript part in the view should listen to the click event and send a GET request to the controller, the controller checks if the data is sent through AJAX, gets the data from DB then returns the response in JSON form, the view is then updated.
Please, does anyone have a working example or can explain?
Simple working example using JQuery:
In you routes.php file:
Route::post('/postform', function () {
// here you should do whatever you need to do with posted data
return response()->json(['msg' => 'Success!','test' => Input::get('test')]);
});
and in your blade view file:
<form method="POST" action="{{ url('postform') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input type="text" name="test" value="" />
<input type="submit" value="Send" />
</form>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript">
jQuery(function ($) {
$(document).ready(function()
{
var form = $('form');
form.submit(function(e){
e.preventDefault();
$.ajax({
url: form.prop('action'),
type: 'post',
dataType: 'json',
data: form.serialize(),
success: function(data)
{
console.log(data);
if(data.msg){
alert( data.msg + ' You said: ' + data.test);
}
}
})
});
});
});
</script>
As you can see, most of the logic is done in JavaScript which has nothing to do with Laravel. So if that is not understandable for you, I'd recommend to look for jQuery ajax tutorials or rtfm :)
I have experienced submitting a modal form without reloading the entire page. I let the user add option to the dropdown and then repopulate the items on that dropdown without reloading the entire page after and item is added.
you can have custom route to your controller that handles the process and can be called by javascript and will return json
Route::get('/profiles/create/waterSource',function(){
$data = WaterSource::orderBy('description')->get();
return Response::json($data);
});
then the javascript
<script>
$(document).on('submit', '.myForm-waterSource', function(e) {
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
data: $(this).serialize(),
success: function(html) {
$.get('{{ url('profiles') }}/create/waterSource', function(data) {
console.log(data);
$.each(data, function(index,subCatObj){
if (!$('#waterSource option[value="'+subCatObj.id+'"]').length) {
$('#waterSource').append('<option value="'+subCatObj.id+'">'+subCatObj.description+'</option>');
}
});
$('#myModal-waterSource').modal('hide');
$('#modal-waterSource').val('');
});
}
});
e.preventDefault();
});
</script>
You can view the full tutorial at Creating new Dropdown Option Without Reloading the Page in Laravel 5

Resources