I am building an angularJS based application and I am running passportjs on my nodeJS back-end.
Authentication works but error handling is not a precise as I want it to be. For example when I am querying my mongoDB and something fails I do the following:
Node:
response.send(406, {error: "Email already in use"});
Angular:
settingsService.saveUserOnServer($scope.settings).then(
function (user) {
//Success
},
function (response) {
console.log(response);
var error = response.data.error;
$cordovaToast.show(error, 'short', 'bottom');
});
This will toast "Email already in use". I want to have the same functionality when using passportjs:
// if no user is found, return the message
if (!user)
return done(null, false, {message: 'No user found'});
This is the response I get in angular:
Object {data: "Unauthorized", status: 401, headers: function, config: Object, statusText: "Unauthorized"}
How can I retrieve the 'No user found' message? Thanks in advance!
Fixed it by using a custom callback:
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
console.log(info);
if (err) {
return next(err);
}
if (!user) {
res.send(401, info);
}
req.logIn(user, function (err) {
if (err) {
return next(err);
}
res.send(user);
});
})(req, res, next);
});
explanation
Related
I'm attempting to use Amazon Passport in my MEAN application for authentication, but I'm running into a cross origin error. My application is set up like this:
View:
<a id="LoginWithAmazon" ng-click="vm.auth()">
<img class="responsive-img amazon-button"/>
</a>
Controller:
vm.auth = function () {
Auth.login()
.then(function () {
$location.path('/');
})
.catch(function (err) {
vm.error = err;
});
}
Service:
vm.login = function () {
var deferred = $q.defer();
$http.post('/auth/amazon')
.then(function (res) {
console.log('SUCCESS! ', res);
deferred.resolve();
})
.catch(function (err) {
console.log('ERR: ', err);
deferred.reject(err.data);
});
return deferred.promise;
};
And in my Express/NodeJS context...
Passport is configured like this:
passport.use(new AmazonStrategy({
clientID: config.amazon.clientID,
clientSecret: config.amazon.clientSecret,
callbackURL: "http://localhost:9000/test"
},
function(accessToken, refreshToken, profile, done) {
console.log('SUCCESS AUTH: ', profile);
process.nextTick(function() {
return done(null,profile);
});
});
}
));
Express route:
router.post('/auth/amazon', function (req, res, next) {
passport.authenticate('amazon', function (err, user, info) {
console.log('Err? ', err);
console.log('User: ', user);
})(req, res, next);
});
When I try to make the authentication request, I am getting the following error:
I've tried using Login with Amazon but to no avail as well. I've whitelisted http://localhost:9000 on Login with Amazon's configuration. Any help would be greatly appreciated.
Looks like you make an AJAX request to an endpoint you should redirect (302) to. The endpoint possibly contains a login page and the user should be able to see it and use it.
Make sure you follow the oauth specs and you don't issue an AJAX request prematurely. The protocol requires you first redirect to their login page and then, only when you have the one-time code, you issue an AJAX request to exchange the code for an access token.
I generated token with JWT using node and angular, and can't check if user is authorized.
Node:
module.exports.authenticate = function(req, res) {
var user = new User(req.body);
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
}
else if (user) {
if (user.password != req.body.password) {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
}
else {
var token = jwt.sign(user, config.secret, {
expiresIn: 60*60*24
});
res.json({
success: true,
token: token
});
}
}
});
};
Angular:
$http(req)
.then(function (response) {
console.log(response.data.success);
if(response.data.success) {
var user = localStorage.setItem('token', JSON.stringify(response.data));
token = localStorage.getItem('token');
// console.log('User info: ', JSON.parse(getuser));
// window.location = "/dashboard";
return response.data;
}
}, function (response) {
}
);
}
How can I check token when I change route?
And generically how can I use Token?
Angular ui-router provides $routeChangeStart event while you change a route. You can use it in the following way.
$rootScope.$on('$routeChangeStart', function (event, next, current){
//you can code here something to be run on changing routes
}
You might want to have a look here for detailed event documentation.
Regarding a more generic implementation , you can create a service to keep your token at the time of login or whenever you get it. Thereafter you can keep getting the token from the service for any future comparisons.
you should install "cookie-parser"
npm i cookie-parser
and go to index.js file and add
const cookieParser = require('cookie-parser');
app.use(cookieParser());
it works for me
I am working on a simple blog website based on angular.js + node.js and mongodb using express template.
I hit with $http from angular controller by POST method to a api named users.js where login is authenticated using passport.authenticate method.
I require passport-local login strategies in users.js.
But it's not working.here is angular login service code and node users api code.
Can anybody tell me how can use passport.js in angular and node?
angular routing through a service
app.service('Auth',function($location,$http,$localStorage){
var userLogin ;
return{
setLogIN:function(email,password){
$http({
method: 'POST',
url: '/users/login', //users.js having node routing.
data: {email:email, password:password},
})
node routing in user
router.post('/login',passport.authenticate('local', {
// use passport-local for authentication
successRedirect : '/profile',
failureRedirect : '/login',
failureFlash : true
}));
passport-local strategy
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(
function (username, password, done) {
User.findOne({username: username}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {alert: 'Incorrect username.'});
}
if (user.password != password) {
return done(null, false, {alert: 'Incorrect password.'});
}
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
function isAuthenticated(req,res,next){
if(req.isAuthenticated())return next();
res.redirect('/');
}
So I want to authenticate using passport, but use the client side templating/routing to keep the proper authentication.
Can someone please point me in the right direction? Or tell me if what I am doing is completely misguided?
edit : the error I AM getting with my code is it's not redirecting to profile page
TypeError: POST http://localhost:3000/users/login 500 Internal
Server Error
Not a valid User
i found solution to my question..
how to use passport with angular-nodejs routing.......
//angular js routing
$scope.userlogin=function(){
$http({
method:"post",
url:'/users/login',
data:{username:$scope.username,password:$scope.password},
}).success(function(response){
$scope.userData = response;
$localStorage.userData = $scope.userData;
console.log("success!!");
$location.path("/profile")
}).error(function(response){
console.log("error!!");
$location.path("/login")
});
}
i use POST method and hit to node (users.js) controller and get response from it. if user authentication is successful then it relocate to profile view otherwise remain on login view.
//add these two lines to app.js
// var app = express();
app.use(passport.initialize());
app.use(passport.session());
//node routing
// add passport-stretegies to users.js
passport.use(new LocalStrategy(function(username, password, done) {
user.findOne({username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (user.password != password) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
// console.log(user)
});
}));
//passport serialize user for their session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
//passport deserialize user
passport.deserializeUser(function(id, done) {
user.findById(id, function(err, user) {
done(err, user);
});
});
//router on same page
router.post('/login',passport.authenticate('local'),function(req,res){
res.send(req.user);
//console.log(req.user);
});
get a hit from angular side throught post method it use passport-local method for authentication if user is authenticated seccessfully then authenticated user is sent as response..
By default, LocalStrategy expects dictionary parameters to be named username and password.
If you want to use email instead of username, then you should define them in your strategy:
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(username, password, done) {
// ...
}
));
For your case, it should be:
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function (username, password, done) {
User.findOne({username: username}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {alert: 'Incorrect username.'});
}
if (user.password != password) {
return done(null, false, {alert: 'Incorrect password.'});
}
return done(null, user);
});
}
));
I'm working on basic authentication for my project in node.js using passport.js and it's LocalStrategy method. It's even without password validation yet. Accounts are stored in MongoDB instance.
I was stuck for whole day when in course I'm going through instructor recommended binding form data to angular and sending $http.post() from there, like so:
$scope.signIn = function (username, password) {
$http.post('/login', {username: username, password: password})
.then(function (res) {
if(res.data.success) {
console.log('Logged in');
} else {
console.log('error logging in');
}
})
};
And here's the route for it:
app.post('/login', function (req, res, next) {
var auth = passport.authenticate('local', function (err, user) {
if(err) { return next(err); }
if(!user) { res.send({success: false, user: user}); }
req.login(user, function (err) {
if(err) { return next(err); }
res.render('index', { success: true, user: user });
});
});
auth(req, res, next);
});
Except it ALWAYS returned with { success: false, user: false }. After ton of googling I've decided to make a POST request directly from form:
JADE:
.navbar-right(ng-controller='navbarLoginCtrl')
form.navbar-form(action='/login' method='post')
.form-group
input.form-control(name='username' placeholder='username', ng-model='username' required)
.form-group
input.form-control(name='password' type='password', placeholder='password', ng-model='password' required)
button.btn.btn-default(type='submit' value="Submit") Sign in
as opposed to:
.navbar-right(ng-controller='navbarLoginCtrl')
form.navbar-form
.form-group
input.form-control(name='username' placeholder='username', ng-model='username' required)
.form-group
input.form-control(name='password' type='password', placeholder='password', ng-model='password' required)
button.btn.btn-default(ng-click='signIn(username, password)') Sign in
Submit approach actually works but i'd like to keep things clean and do it with angular.
How can I do it?
Other passport.js components for reference:
var User = mongoose.model('User');
passport.serializeUser(function (user, done) {
if (user) {
done(null, user._id);
}
});
passport.deserializeUser(function (id, done) {
User.findOne({_id: id}).exec(function (err, user) {
if(user) {
return done(null, user);
} else {
return done(null, false);
}
});
});
passport.use(new LocalStrategy(
function (username, password, done) {
User.findOne({username: username}, function (err, user) {
if (user) return done(null, user);
else return done(null, false);
});
}
));
You should check what your browser send.
Your broswer form send data in the form username=&password=, angular post them in JSON {username:, password:} and the Content-Type header is different.
If you want to do the same in angular :
var headers={ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'};
return $http.post(BackEndpoint+'/login','username='+username+'&password='+password,
{headers:headers}).then(function(result){
});
This is what i use against spring authentication.
I'm trying to retrieve the parameters of a GET request as follows but anything I try logs out as 'undefined':
GET
// Find a list of Players
$scope.find = function() {
$scope.players = Players.query({limit: 50});
};
Middleware
//Players service
angular.module('players').factory('Players', ['$resource',
function($resource) {
return $resource('players/:playerId', { playerId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
End Point
exports.list = function(req, res) {
Player.find().sort('-created').limit(req.body.limit).populate('user', 'displayName').exec(function(err, players) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(players);
}
});
};
A good discussion on GET with message body - Is this statement correct? HTTP GET method always has no message body
In short, the behavior of servers when using message body with GET would probably not be consistent.
As my request is a GET I need to attain the parameters (within the query string) using .query:
Player.find().sort('-created').limit(req.query.limit).populate('user', 'displayName').exec(function(err, players) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(players);
}
});
};
This answer was greatly helpful More info