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);
});
}
));
Related
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'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 using the passport module to authenticate a user and generate a jwt token.
Now I want to make facebook login possible. I'm in the phase that I get a fb token and facebook id.
What am I supposed to do with this info? Currently I make a new user that has a variable with facebook id but without a password.
Without a password I can't generate a jwt token, thus I cannot login.
How does this work?
My front end is angular and my API is written with nodejs, express & mongoose.
Passport code:
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.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
generatejwt code:
UserSchema.methods.generateJWT = function() {
// set expiration to 60 days
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
return jwt.sign({
_id: this._id,
username: this.username,
exp: parseInt(exp.getTime() / 1000),
}, 'SECRET');
};
You need to create 2 routes :
1) This will be called to login to facebook.
2)Callback route if the user authentication is successful.(This callback route needs to be registered in the facebook developer portal.
//Facebook Login
app.route('/auth/facebook')
.get(passport.authenticate('facebook', { scope: 'email' }));
// Callback
app.route('/auth/facebook/callback')
.get(passport.authenticate('facebook'),users.generateJWT);
Define a facebook strategy that will store the user details (id, token, email) in your database
var facebookStrategy = new FacebookStrategy({
clientID: cfg.facebook.clientID,
clientSecret: cfg.facebook.clientSecret,
callbackURL: cfg.facebook.callbackURL,
profileFields: ['id', 'email', 'first_name', 'last_name']
},
function(token, refreshToken, profile, done) {
console.log("Token: "+ token);
console.log("RefreshToken: "+ refreshToken);
console.log("profile: "+ profile);
process.nextTick(function() {
User.findOne({ 'facebook.id': profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
console.log("User Found");
return done(null, user);
} else {
console.log("Creating user");
var newUser = new User();
console.log("Token: "+ token);
newUser.facebook.id = profile.id;
newUser.facebook.token = token;
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
newUser.facebook.email = (profile.emails[0].value || '').toLowerCase();
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
});
In the controller, write the below code for callback route:
/**
* Generate JWT Token
*/
exports.generateJWT = function(req, res) {
var token;
token = req.user.generateJwt();
res.status(200);
res.json({
"token" : token
});
};
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
In my mongodb, there is clearly a User with username "testuser", but I am still getting "failed to login!" when I enter this in. Note: Not using the password yet. Just trying to make sure I can check that the entered username exists first.
navbar-login.jade
.navbar-right(ng-controller="mvNavBarLoginCtrl")
form.navbar-form
.form-group
input.form-control(placeholder="email", ng-model="username")
.form-group
input.form-control(type="password", placeholder="password", ng-model="password")
button.btn.btn-primary(ng-click="signin(username, password)") Sign In
mvNavBarLoginCtrl.js (I keep getting "failed to login!", which means {success:false}, which I believe means the user wasn't found)
angular.module('app').controller('mvNavBarLoginCtrl', function($scope, $http) {
$scope.signin = function(username, password) {
$http.post('/login', {username:username, password:password}).then(function(response) {
if(response.data.success) {
console.log("logged in!");
}
else {
console.log("failed to login!");
}
})
}
});
routes.js (I get the "no user found.")
app.post('/login', function(req, res, next) {
var auth = passport.authenticate('local', function(err, user) {
if(err) {return next(err);}
if(!user) {
console.log("no user found.");
res.send( {success:false} );
}
// we are using XHR and not a form to login so we gotta do this
req.logIn(user, function(err) {
if(err) {return next(err);}
res.send( {success:true, user:user} );
})
})
auth(req, res, next);
})
server.js
var User = mongoose.model('User');
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne( {'username': username} ).exec(function(err, user) {
console.log(user);
if(user) {
return done(null, user);
}
else {
return done(null, false);
}
});
}
));
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);
}
})
});
express.js config (not sure if I did this right with Express 4)
app.set('views', config.rootPath+ '/server/views');
app.set('view engine', 'jade');
app.use(morgan('dev'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({extended:true}));
app.use(session({secret: '<mysecret>', saveUninitialized: true, resave: true}));
app.use(passport.initialize());
app.use(passport.session());
app.use(stylus.middleware(
{
src: __dirname + '/public',
compile: compile
}
));
app.use(express.static(config.rootPath + '/public'));
passport.use(new LocalStrategy...) was not being called.
This has to do with the new Express 4 config. I needed to add bodyParser.json() to express.js:
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.urlencoded({ extended: true })); // parse application/x-www-form-urlencoded
I was unable to find any documentation for this. All of the passport.js documentation seems to be outdated and intended for Express 3 which only requires an app.use(express.bodyParser());. The middleware config is the culprit if anyone else runs into this issue.
Can you check in your passport.js file if the username is undefined or not? Just console.log that.
And if it shows that it is not undefined, check your mongodb database to see if anything is actually saved in there. Grab robomongo to check this. http://robomongo.org/
var User = mongoose.model('User');
passport.use(new LocalStrategy(
function(username, password, done) {
/*Check if these values exist, if they do something is wrong in mongodb*/
console.log('THIS IS THE USERNAME', username);
console.log('THIS IS THE PASSWORD', password);
User.findOne( {'username': username} ).exec(function(err, user) {
console.log(user);
if(user) {
return done(null, user);
}
else {
return done(null, false);
}
});
}
));
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);
}
})
});