I'm so confusing of this forbidden issue. First of all I checked related stackoverflow posts and googled enough but still have no idea.
Project Details:
- Currently used libraries:
1. Carabiner (https://github.com/bcit-ci/CodeIgniter/wiki)
2. Template (https://github.com/jenssegers/codeigniter-template-library)
3. hmvc
3. instagram_api
- CSRF Protection turned on (I don't want false the protection)
- Followed main posts
1. https://stackoverflow.com/questions/40225908/ajax-post-not-working-codeigniter
2. https://stackoverflow.com/questions/22527412/403-forbidden-access-to-codeigniter-controller-from-ajax-request
- ISSUE: 403 Forbidden ("The action you have requested is not allowed.")
HTML
instagram.php
form_open() function generates hidden field for access token and it is included in ajax posting data.
<input type="hidden" name="csrf_token_localhost" value="3f5887fd41ac4eaa9b558afa7cb4a6de">
...
<?php echo form_open('admin/getInstagramAccessToken', array('id' => 'instagram_settings', 'class' => 'form-horizontal row-border')); ?>
...
<div class="col-sm-8 col-sm-offset-2">
<?php echo form_submit('submit', 'Get my access token', ['class' => 'btn-primary btn btn-get-token']); ?>
</div>
...
<?php echo form_close(); ?>
Javascript
adminscript.js
$('#instagram_settings').submit(function( event ) {
var posting_data = $( this ).serializeArray();
event.preventDefault();
$.ajax({
type: 'POST',
dataType: 'json',
async: true,
cache: false,
data: posting_data,
url: 'admin/getInstagramAccessToken',
success: function(json) {
try{
console.log(json);
}catch(e) {
console.log('Exception while request..');
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR, textStatus, errorThrown);
},
complete: function() {
console.log('ajax complete');
}
})
Controller
Admin.php
public function getInstagramAccessToken()
{
if ($this->input->is_ajax_request())
{
$response['status'] = 'success';
echo json_encode($response);
}
else
{
// if the request if not ajax then show 404 error page
show_404();
}
}
When I make the csrf protection status false they all work fine. Or when I changed the post type from "post" to "get". But I want to keep the status true and using "post" method.
Dropbox link for two images:
Hidden csrf token field after form load
Posting Datas
https://www.dropbox.com/sh/e93ubgwzv9zir5j/AAA6vf5IWc1m7rtpGWGCpub4a?dl=0
You can by pass this issue by sending the CSRF token with your AJAX request.
This is occurring because you have put CSRF protection in 'ON' mode.
This can be bypassed only if you sent the request through GET or explicitly put CSRF 'OFF', which of course is not advised.
So the solution is to include the CSRF token in your AJAX requests.
Read this post, to know more about putting CSRF tokens on AJAX requests.
EDIT: Ideally, you shoud send the CSRF token through your data option in ajax requests. Check this link for more info
Please add this snippet in your every form which have type POST.
<?php $csrf = array(
'name' => $this->security->get_csrf_token_name(),
'hash' => $this->security->get_csrf_hash()
); ?>
<input type="hidden" name="<?php echo $csrf['name']; ?>"
value="<?php echo$csrf['hash']; ?>" />
Related
So, I've been looking on here for the better part of 3 hours as to why my code isn't working. I don't think my ajax request is detecting my CSRF token even though i've tried multiple ways to implement it. New to AJAX requests so go easy.
Goal:
Submit an ajax POST request to /email/subscribe/ and add the email address provided by the user to the email_subscribers table.
I've tried adding the following to my code to get the token to show up:
The meta tag and the Ajax Setup.
Top of the file
<meta name="csrf-token" content="{{ csrf_token() }}">
In the script tag INSIDE the jquery $(document).ready() function
// CSRF Ajax Token
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Hidden input field
I've also added a hidden input field and tried to insert the token inside the data object inside the ajax post.
HTML
<input type="hidden" id="token" value="{{ csrf_token() }}">
Javascript tag
var token = $('#token').val();
// var token = $('meta[name="csrf-token"]').attr('content'); //Tried this way
$.ajax({
type: 'POST',
url: '/email/subscribe/',
dataType: 'json',
data: {
email: email,
'_token': token,
// "_token": "{{ csrf_token() }}", //Tried this way as well
"_method": 'POST',
},
success: function (data) {
// Check Server Side validation
if($.isEmptyObject(data.errors)){
console.log(data['success']);
}else{
// Validation Failed Display Error Message
console.log(data.errors);
}
},
error: function (data) {
console.log(data);
}
}); // End Ajax POST function
Here is my web.php file
// Email Routes
Route::prefix('email')->group(function() {
// Create Post Route for subscribing
Route::post('/subscribe', 'EmailSubscriptionsController#subscribe')->name('email.subscribe');
});
and my EmailSubscriptionsController
class EmailSubscriptionsController extends Controller
{
// Store the Email into the database
public function subscribe(Request $request) {
// Validate the request
$validator = Validator::make($request->all(), [
'email' => 'required|email',
]);
// If the validation fails
if($validator->fails()) {
return response()->json([
'errors' => $validator->errors()->all(),
]);
}
// New Section Object
$subscription = new EmailSubscription;
// Add Name into Section Object
$subscription->email = $request->email;
// Save the Section
$subscription->save();
// Return The Request
return response()->json([
'success' => 'Record has been saved successfully!'
]);
} // End Subscribe
} // End Controller
Whats really weird is the fact that when i submit the request with NOTHING but the following in my subscribe() function inside my controller:
// Return The Request
return response()->json([
'success' => 'Record has been saved successfully!'
]);
It doesn't return an error.... Just passes the success message to the console. I'm not sure what i'm doing wrong as I have this working (an ajax post request) in another portion of my site.
Start by looking in storage/logs/laravel.log for the exception stack trace. That should give a more clear indication of what is failing.
The web inspector also allows you to see the response which usually includes the trace.
A common cause of 500 ISEs is improperly importing classes via use.
Use It Like This:-
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
I'm trying to parse the data that I send from my ajax form. But for some reason I cannot extract the information from the form in the php controller. Here's what I'm doing.
Here is the html:
<div style="margin-top: 100px;">
<h2>Character settings</h2>
<table class="table table-striped table-bordered table-hover">
<tr>
<th>Name</th>
<th>Map</th>
<th>Move</th>
</tr>
#foreach($chars as $char)
<tr>
<td>{{$char['name']}}</td>
<td>{{$char['map']}}</td>
<td>
{{Form::open(array('action' => 'UsersController#move', 'class' => 'mover'))}}
<input type="hidden" name="charID" class="charID" value="{{$char['id']}}" />
<button type="submit" class="btn btn-small btn-info">Move</button>
{{Form::close()}}
</td>
</tr>
#endforeach
</table>
Here is the javascript ajax processing:
$('#mover').on('submit', function(e){
e.preventDefault();
var $form = $( this ),
method = $form.attr( "method" );
$.ajax({
url: "{{action('UsersController#move')}}",
dataType: "json",
data: $form.find('.charID').val(),
type: method,
success: function (response) {
console.log(reponse['test']);
}
});
});
Here is the controller:
public function move() {
if(Auth::check()) {
Log::info(Input::get('charID')); //When I check the logs this is blank
$person = Character::where('id', '=', Input::get('charID'))->first();
$person->map = 100000000;
$person->save();
$response = array('status' => 'success', 'text' => 'Your character has been moved!');
return Response::json($response);
exit();
}
else {
return Redirect::action('PageController#showHome');
}
}
When I check the console log on submit I see the data "charID", so its being extracted by the form correctly, but I can't seem to get it in the laravel controller. Strange thing is I use the Input::get() function in other parts of my controller. So it's just this function.
Any help appreciated!
You're accessing response[test] instead of response[text].
Other things of note:
Your use of exit(); is redundant and will never be hit because you are returning above it.
Also, exit is a language construct and it can be called without parens if no status is passed. So if you are to use it without an argument, just use exit;.
Your ajax method could be optimised a bit more and it seems you also have another problem with your data, which needs to be sent as a key value pair.
You could do it as an inline string in the form of a GET request, or as an object like { "charID": $(this).find("input[name=charID]").val() },.
$("#mover").on("submit", function (e) {
e.preventDefault();
$.ajax({
"url": this.action, // <-- we can use native javascript
"dataType": "json",
"data": $(this).serialize(), // <-- collects your form data
"type": this.method,
"success": function (data) {
if (data.success !== "undefined" && data.success === 200) {
console.log(data.text);
}
}
});
});
I would recommend against using the Form helper class. It isn't very readable after-the-fact. You don't save that much time using it compared to writing out the HTML form definition anyway.
Let's optimise your controller so it's easier to understand as well:
public function move ()
{
if (Auth::check()) {
Log::info(($charID = request('charID')));
$person = Character::where('id', '=', $charID)->first();
$person->map = 100000000;
$person->save();
return response()->json([
'success' => 200, // success
'text' => 'Your character has been moved!'
]);
} else {
return response()->json([
'success' => 401 // unauthorised
]);
}
}
For a variable you're accessing more than once, I use a neat trick with parens (). If you wrap an assignment in parens e.g. ($charID = request('charID')) you can still use it's output in-line, with the added benefit that you now have access to it further into your script.
I'm opting for the helper methods like request() and redirect() instead of their class counterparts and the use of array() is a bit old hat - but that's just my opinion - I'd use square brackets [].
Your response when the user isn't authenticated is useless here assuming this controller action is only meant to handle posts from AJAX requests (if I'm not mistaken). Since it can't force your asynchronous request to redirect you. Instead we return the proper HTTP response code 401 to represent that the user was unauthorised.
You could also look up PSR-2 standards for your code structure, as that is what is used as an industry standard nowadays.
Lastly, if you are using the web middleware (with CSRF protection) on the controller here. You need to send a CSRF token with the request. You can do that by popping {{ csrf_field() }} into the form. The form helper may be doing this for you.
But another problem you would have stumbled into if you were not using $(this).serialize() in the AJAX setup is that the _token field would never have been sent along with the request.
Try
data: { 'charID': $form.find('.charID').val() },
as it is now you're only sending the value, there's no way PHP let alone Laravel will know its name
I see a few potential issues with this code.
You're initializing your form with 'class' => 'mover', but your jquery is looking for $('#mover') - So your form tag has the class 'mover' but your jquery expects an id 'mover'
You're setting the ajax method to $form.attr( "method" ); but I'm not sure if method is getting set at all in your form tag? Why not just set your ajax type/method to POST since that's the appropriate method for posting a form?
try to change as below,
$.ajax({
url: "{{action('UsersController#move')}}",
dataType: "json",
data: $form.serialize(),
type: 'post',
success: function (response) {
console.log(reponse['test']);
}
});
Hi guys? am trying to post data to the database using laravel 5 and ajax..am also applying using csrf protection by adding
<meta name="_token" content="{!! csrf_token() !!}"/>
to my layout header and adding the following code to my footer:
<script type="text/javascript">
$.ajaxSetup({
headers: { 'X-CSRF-Token' : $('meta[name=_token]').attr('content') }
});
</script>
This is my form:
<form action="{{action('QuizController#postQuiz')}}" method="POST">
<div id="name-group" class="form-group">
<label for="name">Please type your question here</label>
<input type="text" class="form-control" name="question">
</div>
<button type="submit" class="btn btn-success">Submit <span class="fa fa-arrow-right"></span></button>
</form>
This is my JS code:
var formData = {
'question' : $('input[name=question]').val(),
};
// process the form
$.ajax({
type : 'POST',
url : 'quiz',
data : formData,
dataType : 'json',
encode : true
})
// using the done promise callback
.done(function(data) {
// log data to the console to see
console.log(data);
// ALL GOOD! just show the success message!
$('form').append('<div class="alert alert-success">' + data.message + '</div>');
// stop the form from submitting the normal way and refreshing the page
event.preventDefault();
This is my route:
Route::post('create/quiz', array(
'as' => 'post-quiz',
'uses' => 'QuizController#postQuiz'
));
When my controller is like the following:
public function postQuiz()
{
if(Request::ajax()) {
$question = Request::get('question');
$data['success'] = true;
$data['message'] = $question;
echo json_encode($data);
}
the ajax call works and it returns,
Object {success: true, message: "test question"}
but when I try posting data to the database using:
public function postQuiz()
{
if(Request::ajax()) {
$question = Request::get('question');
DB::table('questions')->insert([
'question' => $question,
]);
}
I get the following from the console
POST http://localhost/leoschool-laravel5/public/create/quiz 500 (Internal Server Error)
and
Object {readyState: 4, responseText: "{"success":true,"message":"test question"}<!DOCTYPE htm…l>↵</div>↵↵ </div>↵ </body>↵</html>", status: 500, statusText: "Internal Server Error"}
What could be the problem? Thanks..
A good place to start is with Chrome Developer tools. Load your page with the tools open and fire the event that does the AJAX request.
Under the network tab of the tools, it will show you every request made and allow you to preview the response as if you were not using AJAX. This will show you the laravel stack trace. I think the problem is that you're using facades and they're not namespaced correctly.
Change your controller function to this and see if it works:
public function postQuiz()
{
if(\Request::ajax()) {
$question = \Request::get('question');
\DB::table('questions')->insert([
'question' => $question,
]);
}
With the above instruction on how to use dev tools and with the corrected code, you should be able to fix your problem. A better way to write this code would look like this though:
// assuming you have these models setup
// this uses dependency injection
public function postQuiz(Request $request, Question $question)
{
if($request->ajax()) {
$newQuestion = $request->get('question');
//add fields here to create new question with
$question->create([ /*stuff*/ ]);
}
I'm implementing a like/dislike feature using laravel. I got everything up and running except for the asynchronous request. I only want logged in users to be able to like/dislike an item so the request has to be verified server side. I also want it to be an asynchronous request, so no page refresh.
What would be a good way to do this? I already have jquery loaded so using angular JUST for that would be a bit of an overkill. How would I protect the request? I have read things about tokens, but I'm not quite sure about that.
The best way to do this would be to add a csrf filter to your config.php file. Laravel should generate csrf values for you by default but if not you can use it the following way with blade.
{{ csrf_token() }}
Your ajax could look something like this below. Note the csrf token could also be in your header tag as well but I put it here because of the way my code worked.
$.ajax({
type: "POST",
beforeSend: function(request) {
return request.setRequestHeader('X-CSRF-Token',"{{ csrf_token() }}");
},
url: baseLocalUrl, //this would be your path to your route
data:
{
html: sendHTML //data separated by a comma
},
success: function(data){
alert("Success");
},
error: function(xhr, textStatus, thrownError) {
alert('Failed');
}
});
For me on my last project, I have a bunch of forms and some ajax with csrf so I had to change the config filter to this because I had trouble mending the two so I created the following filter below.
Route::filter('csrf', function()
{
$token = Request::ajax() ? Request::header('x-csrf-token') : (Input::get('csrf_token') ?: Input::get('_token'));
$test1 = Session::getToken() != Input::get('csrf_token') && Session::getToken() != Input::get('_token');
$test2 = Session::token() != $token;
if($test2 && $test1){
throw new Illuminate\Session\TokenMismatchException;
}
});
Reference with more details
So I am trying to submit just an email address using ajax to get people to register and I have no idea why I am getting a 500 internal server error. I am new to ajax calls.
I have tried to follow the following tutorial: http://www.youtube.com/watch?v=TZv5cgua5f0
However I have done as they have said and still if I do a post with values I do not get to the desired controller method. If I do add data to the post then I get an internal server error.
javascript:
$('#email_submit').click(function()
{
var form_data =
{
users_email: $('#users_email_address').val()
};
$.ajax
({
url: 'http://localhost/myZone/NewsLetter/submit',
type: 'POST',
data: form_data,
success: function(msg)
{
alert(msg);
}
});
return false;
});
HTML
<div id="email_newsletter_signup" class="ajax_email_block_signup" >
<h3>Sign up to the newsletter:</h3>
<?php echo form_error('signup_email','<div id="email_error" class="error">','</div>');?>
<h3>email: <input id="users_email_address" type="email" name="signup_email" value="<?php echo set_value('signup_email'); ?>" placeholder="Your email"/> </h3>
<input id="email_submit" type="submit" name="submit"/>
</div>
contoller
public function index()
{
Assets::add_module_js('newsletter','email.js');
//If included will be added
Template::set_block('email_block','email_block');
Template::render();
}
public function submit($email)
{
$success = $this->newsletter_model->set_unverified_email($email);
// if($success === FALSE)
// {
// Template::set_block('newsletter_error','newsletter_error');
// }
// else
// {
// Template::set_block('newsletter_success','newsletter_success');
// }
// Template::render();
return;
}
I have a breakpoint inside the submit and it just wont be hit when I do a post
Thanks
Found my solution. Nothing to do with bonfire but with the codeigniter. It was the CSRF token.
Here is an excellent post about sorting the issue:
http://aymsystems.com/ajax-csrf-protection-codeigniter-20
add csrf token to data before posting
$.ajax({
type: "POST",
url: url,
data: {'<?php echo $this->security->get_csrf_token_name(); ?>':'<?php echo $this->security->get_csrf_hash(); ?>'}
})
csrf token needs to be send along every request so it needs to be specified by the above echo statements