I'm working on a login/out functionality for my web application. When the user logged in my NodeJS(Express) server sets cookies variable called "user" on .mydomain.com with path '/'. Then I use AngularJS to access user cookies. It reads it fine from any page under my domain, but when I want to log the user out. I try deleting the cookies value, but it doesn't delete it on any other pages than the index page ('/').
I know there's something to do with the cookies path, but as far as I know it's ok to read/write cookies with path '/' form anywhere in the same domain. Is that not the case?
Setting the cookies, NodeJS
res.cookie('user', JSON.stringify(response.user), {
expires: moment().add('d', 2).toDate(),
path: '/',
domain: '.mydomain.com'
});
Deleting the cookies from /myPage/page2, AngularJS
delete $cookies['user'];
Is there anyway this could work?
Thanks!
I usually post to the server.
So, on the client side, I have something like:
$scope.logout = function() {
$http.post('/logout').then(function(){
$state.go('login');
});
};
and on the server side:
app.post('/logout', function(req, res, next) {
delete req.session.user
req.session.regenerate(done);
res.json(200, 'OK');
});
Related
I'm currently getting started with Sails.js, and I want to add user accounts to my toy app, so I installed the "sails-auth" package that creates a Passport-based user authentication system. I can create new users by sending POST /user, and I can sign in with POST /auth/local.
The documentation says:
Authenticate with the local strategy via a POST to /auth/local with params identifier (email) and password). This will also create a session. See passport.local for more.
However, when I try to GET /user/me, which routes to a controller action that should return the current user in the session, the page instead gives an empty response. Why is this happening? Is there some kind of configuration step that I'm missing?
By the way, I haven't changed or messed around with the sails-auth package. It's still completely new; the "me" action looks like this:
me: function (req, res) {
res.ok(req.user);
}
EDIT: I've found a temporary workaround by searching the issues in the sails-auth repo. Instead of getting a user object from req.user, you can get a string user ID from req.session.passport.user.
Your me action as written is only going to return whatever you are passing in as the user param. Sails builds on top of Express.js so req is the request from the browser and res is the response to the browser.
Most likely you are sending the data to your me action in the req body which is why your response is blank, simply put, req.user is empty so the response is empty. In that case you would access it with req.body.user, you could also try var user = req.params();
For debugging and just generally getting a feel for how the req and res objects are structured I suggest you always start sails (in development, never in production) with the verbose flag.
sails lift --verbose
Then you can do this:
me: function(req, res){
sails.log.verbose(req);
res.ok(req.user);
}
And have it print out the entire req object so you know what's in req.user.
Typically though you would do a database lookup as the user param would be an id. Which means your me function might look (something, obviously depending on your dbc it might be pretty different) like:
me: function(req, res){
var userId = req.body.user;
User.find({'user_id': userId}.exec(function(err, user){
if(err){
//tell peeps there was an error
}else{
res.ok(user);
}
});
}
Best debugging for routes and for the request object:
'/*' : function(req, res, next) {
sails.log.verbose("method: ", req.method, "\n body: ", req.body, "\n url:", req.url);
next();
},
Just paste that at the start of your routes module.
I've seen a few questions regarding this but being new to both Angular and Node I'm struggling to find a proper solution.
I had this code, which worked for the login and I could only access my pages after being authenticated:
router.post('/login',
passport.authenticate('local',
{
successRedirect: '/',
failureRedirect: '/login',
failureFlash: false,
successFlash: false
}
));
router.all('*', ensureAuthenticated);
function ensureAuthenticated(req, res, next) {
console.log("in ensureAuth", req.isAuthenticated());
console.log("session data", req.session);
console.log("user data", req.user);
if (req.isAuthenticated())
{
return next();
}
res.redirect('/login');
}
The local passport authentication I have returns an object after verifying if the user exists and allows login, as such:
return passportDone(null, result.rows[0]);
with result.rows[0] being the user info I want.
What I had in the front end is a simple ng-submit in the form that calls the "login(credentials)" with credentials being set by ng-model in their respective fields (username and password).
My question is how can I return all the info I want, such as user_role, name and stuff, so I can present it in the front-end as {{user.name}} for example?
The info needs to stay after refreshes so $scope isn't an option from what I've read.
What you probably want to do here, is create a custom route for fetching this information. for example: "/me".
In this route, you can serve the information you now probably store in your session variable.
Another solution, depending on how your application works with authentication, is to inject the userinfo (For example if you log in, and get redirected to a new page, you can inject the userinfo into the new page as a js variable) or to return the userinfo in the response if you send a ajax request to login and dont get redirected to a new page by the server.
I hope you are using passport.js local authentication.I would recommend to store user infomation after authentication in cookies as a json webtoken or using some other encryption.And you should expose an api(/api/is-authenticated) which is used to check whether the user is authenticated, by sending the token stored in cookies.when ever you refresh or navigate to other routes make an api(/api/is-authenticated) call to check whether that particular user has already authenticated or not.
Ok, I got it.
I already had the service and all but the problem was with passport's successfulRedirect. It returns the html you want when it succeeds, which is fine. However, since I wanted the user info, it wasn't enough. What I did was create an /account route that returns the req.user info, which I then handle in the front end and form a cookie with it. What basically happens is:
post_login->verify_user->success->get_account->form_cookie->render_html
I am using passport.js for authentication. My requirement is that, anyone should not be able to access a particular page (say, '/restricted'), if one is not logged in.
Right now, in my application, anyone can access "localhost:3000/#/restricted" url directly.
I am able to stop this and allow only logged in users to access the page by using Rorschach120's solution in
Redirect on all routes to login if not authenticated.
But this is done client side and is not that secure, because anyone can access this code from browser.
So I need that the request for my page goes to server, I tried moka's solution in How to know if user is logged in with passport.js?:
In app.js:
app.get('/restricted', loggedIn, function(req, res, next) {
// req.user - will exist
// load user orders and render them
});
where the loggedIn() function checks if user is logged in or not.
But this middleware is NEVER called and anyone can still access the "restricted" page.
What can I do, so that this gets called?
I am new to AngularJS and NodeJS. Am I doing something wrong here?
Any help will be appreciated.
You can use middleware for that purpose.
app.get('/secure-route', secureMiddleware, myMethod)
let secureMiddleware = function(req, res, next) {
authCheck(...)
.then(function(result) {
// pass
next()
})
.catch(function(err) {
res.status(401).json({
code: 401,
message: 'restricted route'
})
})
}
Many subdomains are forwarded to subdomain X where my meteor app is working.
I'm using code which doesn't work:
WebApp.connectHandlers.use(function(req, res, next) {
console.log(req.connection.domain); // is null
console.log(req.server.domain); // is null
next();
});
Trying also to use IronRouter on server:
Router.map(function () {
this.route('testRoute', {
where: 'server',
path: '*',
action: function () {
console.log(this.request.connection.domain); // null
}
});
});
I know that FastRender injects headers and it works , but the question is :
How can I get subdomain from which user accesses meteor app using WebApp.connectHandlers or IronRouter ?
In the first example, log req.headers to console as well. One of the strings there should contain the original request domain. However, that might depend on how your proxy is configured.
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.