I'm trying to solve this problem. I use Node Express and jQuery.
The user has been logged out (from another page of the site), but is trying to send an ajax request. How to redirect it to the login page?
I can throw an error like so:
if (!req.isAuthenticated()) {
return res.json(function() {});
}
and after on the client side:
$.ajax({
type: 'GET',
...
error: function() {
document.location.href = '/logout';
}
})
But can it be done in some other way?
For example, if you do not use Ajax, then I do this:
if (!req.isAuthenticated()) {
return redirect('/login');
}
Something similar but with Ajax?
Since you mentioned you use Express, you can call the redirect function on the response:
if (!req.isAuthenticated()) {
res.redirect("/login");
}
Related
Axios POST request sends data to Express sever but Error 404
Hello, world, I am trying to build a user authentication server for a project I am working on, but I am running into a problem trying to send a POST request to my Node.js Express server.
I want to send a POST request using Axios containing a username and password from the browser. But once sending the request it gives me a 404 Not Found error. The request has to go to http://website/api/login and my Node.js code should return either "authed" or "invalid". I tested the API inside Postman and that seems to be working. I also exported the request code from Postman and tested it with fetch API, xhr, and Axios, all returning the same result.
The server receives the data and handles it properly, but when I look in the Chromium debugger it appears that the request URL is just http://website/ and not http://website/api/login. I am honestly lost and I have tried what feels like everything, but I can't seem to make it work. Any help in pointing me in the right direction would be amazing! Thank you!
The code I use for the POST request is:
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const data = JSON.stringify({"username": username, "password":password});
const config = {
method: 'post',
url: 'http://website/api/login',
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config).then(function (response) {
console.log(JSON.stringify(response.data));
}).catch(function (err) {
console.log(err);
})
}
This is what I see in the Chromium debugger:
Headers
This is my Node.js / Express code:
app.post('/api/login', function (req, res, next) {
scriptFile.authUser(req.body, function (err, state) {
if (err) console.log(err);
else {
if (state) {
res.send("authed");
} else {
res.send("invalid");
}
}
});
})
Thank you for any help I can get.
I am stupid,
Breakdown of what happened:
Everything was working fine except that I put the input data and submit button inside a form, which will refresh the page...
I fixed it by changing the form to a div.
Hey checking your chrome console pic looks like your post request is hitting the root api address 'http://website/' and not the full path 'http://website/api/login
I create an Ajax request (below) which passes data to the server with the information I need.
function verify(key) {
$.ajax({
async: true,
type: 'GET',
url: '/zonepass/'+key,
data: {
'zone_key': key
},
success: function(res){
//alert('Sent a text message successfully to ' + res);
}
});
}
I handle the Ajax request on the server side where I use the passed in data to query my Firebase DB to get other relevant information.
I then try to render the view page that I want to navigate to using res.render('verify',{zone: obj, key: zone_key}) where verify is another .ejs file that I want to navigate the user to and the JSON object is the data that I want to pass to that template.
My code is not rendering the view page and I'm not sure why. I console logged all the data on the server and all the data is being pulled properly but then my view page never navigates to the verify ejs file...
app.get('/zonepass/:id', function(req,res) {
var zone_key = req.param('zone_key');
var zone_obj = firebase.database().ref('zones').child(zone_key).once('value').then((snap) => {
obj = snap.val();
res.render('verify',{zone: obj, key: zone_key});
});
});
res.render will not work with an ajax request, response from ajax call is returned and accessible inside the success function, but res.render will not work also res.redirect will not work with ajax request.
So you need to submit your request using a form or redirecting on frontend to that route, which is technically also a get request but without ajax example:
Using only HTML:
Verify
Using javascript:
function verify(key) {
window.location.href= "/zonepass/"+ <your id> + "?zone_key=<your zone key>"
}
Also in your NodeJS route you can access id using req.params.id and zone_key using req.query.zone_key, so your server code will be:
app.get('/zonepass/:id', function(req,res) {
var id = req.params.id;
var zone_key = req.query.zone_key;
var zone_obj = firebase.database().ref('zones').child(zone_key).once('value').then((snap) => {
obj = snap.val();
res.render('verify',{zone: obj, key: zone_key});
});
});
BTW you will need to handle inside the verify view, if the key is not verified, example you show an error or message in verify view, that the key is not correct ... or any message related to your logic
I have a settings page that you can insert and delete some filters. For delete request I used this:
$('#delete-filter').click(function (e) {
var filtername = $('#filter-list').val();
var filterCount = $('#filter-list option').length;
var retVal = confirm("Are you sure to delete this filter?");
if( retVal == true ){
$.ajax({
url: "/settings?filtername=" + filtername,
method: 'DELETE',
dataType: "json",
success: function (result) {
}
});
}
else{
return false;
}
});
And here is my route for this page:
router.delete('/settings', ensureAuthenticated, function (req, res, next) {
var promise = user.deleteFilter(req.session.user_id, req.query.filtername);
var promise2 = promise.then(function (data) {
req.session.selected_filter = data.selected;
res.redirect('/settings');
}, function (error) {
console.log(error);
})
})
Essentially I want to redirect to page to settings, so the page reloads with the new data. But in promise chain I can't use any response functions. Am I using the redirect wrong? or I can't send a response in promise chain ?
You've misidentified the problem.
When you issue an HTTP redirect, you say "The thing you were looking for? Get it from here instead."
This is not the same as "The browser should display this URL as a new page".
The HTTP redirect is followed, the settings page is delivered to the browser, then the browser makes it available as the result in your success function. (You then completely ignore it as you haven't put anything in that function).
If you want the browser to load a new page, then you need to deliver the URL as data (not as a redirect) and then assign that value to location.href.
The settings page probably shouldn't be determined dynamically, so you can probably just hard code the URL into the success function.
Hard coding it would make more sense, since you shouldn't send a redirect in response to a DELETE request:
If a DELETE method is successfully applied, the origin server
SHOULD send a 202 (Accepted) status code if the action will likely
succeed but has not yet been enacted, a 204 (No Content) status
code if the action has been enacted and no further information is
to be supplied, or a 200 (OK) status code if the action has been
enacted and the response message includes a representation
describing the status.
imagine user click on something and a modal dialog appear, he done key in the things and the modal close. What should I do next after I send the data back to the db? I do res.redirect('store') but it does nothing, I know what's the problem because I'm already on localhost:2000/store.
I was expecting a refresh because the user just added / updated the data. How will you do it in your case? I'm using node with express 4.
Are you using Ajax to send data from modal to the server? In this case redirect you send from server will not work, it the Ajax request will be redirected only. If you want to refresh the page you need to do this on the client after you receive the response for your request.
Could you provide more details so I can provide a complete answer?
In my frontend modal code using Angularjs, I redirect the client only when I successfully get data back from the server using 'window.loction'
Client code (angular):
$http({ method: 'POST', url: '/login', data:user
}).then(function successCallback(response) {
if (response.status===200) {
$uibModalInstance.dismiss('cancel');
window.location.href = '/home'; <<< redirect user here
} else {
console.log('unexpected response:', response)
}
}, function errorCallback(response) {
if (response.status === 401) {
console.log("scope:",$scope)
$scope.loginform.notauthorised = true;
}
console.log('err: ', response);
});
I have read a few articles around the web and even implemented a few, but for the most part following along is quite tedious and gets a bit off track from my base code causing confusion and wasted time.
That said I know I am close with how I have things implemented, I just need access to the req.user object which I am pretty sure is stored in a session variable. I want to provide what I have done and figure out how to make that extra push in getting a user login session to stick onto my single page abstracted app (cordova/phonegap).
On the server side I am using (node.js, express.js, and passport.js). I generally will allow express to render the views, but since I am using a CORS abstracted app I don't want to send the template to the client over an AJAX call, so I built the views on the client side, basically at this point regardless of what HTML is rendered I realized I just need to have one AJAX POST call to login the user, to invoke the POST route on my server which authenticates the user.
$(document).ready(function() {
$('body').on('submit', '#logIn', function(e){
e.preventDefault();
var formData = $(this).serialize();
$.ajax({
url: "http://mysite.io:3300/login",
data: formData,
type: "POST",
crossDomain: true,
dataType: "json",
success: function(data, textStatus, jqXHR) {
alert('succeeded!');
console.log('success')
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
console.log('error')
}
});
});
});
My question at this point has to do with the passport req.user object. In the client side of the CORS app, the session isn't intact it does not seem because the client relies on an api call that only a logged in user can access, I have this route.
me: function(req, res) {
if(req.user) {
var admin;
if(req.user.admin === true) {
admin = true;
}
var user = {
admin : admin,
user_id : req.user.id,
name : req.user.local.name,
email : req.user.local.email,
}
res.json(user);
} else {
res.redirect('/login');
}
}
I also have other routing methods that rely on the req.user object. So the initial POST method returns the user object, like the name etc, because I have a routing method that looks like this.
//cordova post method
app.post('/login', passport.authenticate('local-login'), function(req, res) {
if (req.user) {
console.log('is user');
res.send({ user: req.user });
}
else
console.log('is not user');
});
Within this method the req.user returns true giving me access to the req.user object and the template, so when making the ajax POST call I am able to render the user profile. After this or on different routing calls that object is false.
So again my question is how can I save the req.user object so I can access it in other methods so that the app knows YES the user is logged in.. obviously that is stored in the session variable, but I am confused upon implementing it?
I don't know how you're handing CORS server-side, but you may want to look into using the cors middleware to make things easier. Either way, you need to make sure you have the Access-Control-Allow-Credentials header set to the value of true.
For the cors middleware, all you have to do is set credentials: true in the cors middleware config. Otherwise you can set the header via res.header('Access-Control-Allow-Credentials', 'true');
You also will need to set withCredentials to true wherever you do any ajax calls so that the cookies will be sent. For $.ajax you would do:
xhrFields: {
withCredentials: true
}
Angular has a similar setting for it's $http service.
After that, you should see your req.user get auto-populated for authenticated sessions.