Pushing user data from Nodejs server to Angular after successful login - javascript

I try to login my user via Facebook with use of PassportJS and pass the user data to Angular.
On the server side it all looks ok with the following code for the Facebook callback in users controller:
exports.facebookCallback = function() {
return function(req, res, next) {
passport.authenticate('facebook', function(err, user, email) {
if (err || !user) {
return res.redirect('/auth');
}
req.login(user, function(err) {
if (err) {
return res.redirect('/auth');
}
return res.redirect('/');
});
})(req, res, next);
};
};
From what I understand from the PassportJS docs, calling req.login should put user data into the session.
My routes on the server side looks following:
app.get('/auth', usersCtrl.auth);
app.get('/auth/signout', usersCtrl.logout);
app.get('/auth/facebook', passport.authenticate('facebook', {
scope: ['email', 'user_hometown']
}));
app.get('/auth/facebook/callback', usersCtrl.facebookCallback());
express and passport configuration includes:
app.use(express.cookieParser());
app.use(express.session({secret: '1234567890QWERTY'}));
app.use(express.bodyParser());
app.use(passport.initialize());
app.use(passport.session());
Now on the angular side I try to get the user data from the session in a service defined like this:
module.exports = require('angular')
.module('HomeModule', [])
.controller('HomeCtrl', function ($scope) {
//home controller code ors here
}).controller('NavbarCtrl', ['$scope', 'Authentication', function ($scope, Authentication) {
$scope.authentication = Authentication;
//rest of the navbar controller goes here
}]).factory('Authentication', [
function() {
var _this = this;
_this._data = {
user: window.user
};
return _this._data;
}
]);
Unfoortunately, the user data is not available in window.user on angular side.
Any ideas what I'm doing wrong here?

As Girish said, the passport session object won't be available on client side. As you seem to be using express, a simple way to do this is to use express-expose.
If you want the user data to be available on all pages when the user is authenticated, you can add something like this before your routes declaration
app.use(function (req, res, next) {
if (req.isAuthenticated()) res.expose(req.user, 'user');
next ();
});
The user data will be available client side as window.user.

The passport session object won't be available on the window object , instead you need to get it from the server using some service or a redirect url.
After successful authentication , the primary route function will be called,
which, in this case, will redirect the user to the home page.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
or you can create a route to get the logged in user data
app.get('/account', function(req, res){
if (req.isAuthenticated()) {
res.send({user : req.user});
}else{
res.redirect('/login');
}
});
On the Angular side, you can set the user data to rootscope from the $http response,
$rootScope.session = {}
$rootScope.session.user = res.user;

Related

adding additional data to req.user with passport-google-oauth

I have a route which meant to authenticate the user using google oauth passport strategy ,(/auth/google) I also want to pass additional data as query in the url in that route (/auth/google?someParam=SOME_PARAM) , this data I want to add to req.user by the time I get it back from google in (/auth/google/callback). The problem is that I have access to this query through /auth/google but google will redirect me to /auth/google/callback which dont have access to this data anymore.
note - Because of design limitation I cant do it with external source as database.
passport-google docs
CODE :
// auth.js
router.get(
"/",
(req, res, next) => {
let siteName = req.query.siteName;
let pageName = req.query.pageName;
console.log("siteName", siteName);
return next();
},
passport.authenticate("google", {
scope: ["https://www.googleapis.com/auth/plus.login"]
})
);
module.exports = router;
// authCb.js
router.get(
"/",
passport.authenticate("google", {
scope: ["https://www.googleapis.com/auth/plus.login"],
failureRedirect: "/"
}),
(req, res) => {
console.log(req.user);
res.send(req.user);
}
);
module.exports = router;
// app.js
app.use("/auth/google", auth);
app.use("/auth/google/callback", authCb);
You have to store your params in session before sending auth request to google.
Then, after redirect, get your params back from session.
// auth.js
router.get(
"/",
(req, res, next) => {
req.session.lastQuery = req.query;
return next();
},
passport.authenticate("google", {
scope: ["https://www.googleapis.com/auth/plus.login"]
})
);
module.exports = router;
// authCb.js
router.get(
"/",
passport.authenticate("google", {
scope: ["https://www.googleapis.com/auth/plus.login"],
failureRedirect: "/"
}),
(req, res) => {
const { lastQuery } = req.session;
console.log(lastQuery);
}
);
module.exports = router;
You should refer to Google's documentation. You can use the "state" parameter to pass any data you want to get back once the user is back to your site. This is the main use of this parameter.
You can see the details here.

Trouble with authentication

I´m facing a confusion issue while implementing the authentication for my restful api using passport local strategy.
Note:
I got the authentication working successfully when I´m doing it all in my index.js. But I want to use in Classes for better Code separation.
I have a passport.js Module
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var mysql = require('mysql');
var dbconfig = require('./database');
var connection = mysql.createConnection(dbconfig.connection);
module.exports = function(passport) {
// passport needs ability to serialize and unserialize users out of session
passport.serializeUser(function (user, done) {
//console.log("SER");
console.log(user),
done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log("XXXX");
console.log(user);
connection.query("SELECT * FROM users WHERE name = ? ",user.name, function(err, rows){
console.log("DER");
console.log(rows);
done(err, rows[0]);
});
});
// passport local strategy for local-login, local refers to this app
passport.use('local-login', new LocalStrategy(
function (username, password, done) {
console.log("hhh");
console.log(username);
connection.query("SELECT * FROM users WHERE name = ? ",username, function(err, rows){
console.log(rows);
return done(err, rows[0]);
});
})
);
// route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
};
This is my Controller Class:
class AuthenticateController {
constructor(router, passport) {
this.router = router;
this.registerRoutes();
this.passport = passport;
}
registerRoutes() {
this.router.post('/login/:username/:password', this.login.bind(this));
//this.router.get('/logout', this.logout.bind(this));
this.router.get('/content', this.content.bind(this));
}
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
res.redirect("/content");
}
content(req, res ) {
console.log(req.user);
if (req.isAuthenticated()) {
res.send("Congratulations! you've successfully logged in.")
} else {
res.sendStatus(401);
}
}
isLoggedIn(req, res, next) {
console.log(req.user);
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
}
module.exports = AuthenticateController;
The Controller gets the router and passport fully configured as parameters from my index.js.
//index.js
var express = require('express')
, cors = require('cors')
, app = express()
, passport = require('passport')
, morgan = require('morgan');
require('./config/passport')(passport); // pass passport for configuration
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(require('express-session')({secret: 'vidyapathaisalwaysrunning',
resave: true,
saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
var apiRouter = express.Router();
app.use('/api', apiRouter);
//
var apiV1 = express.Router();
apiRouter.use('/v1', apiV1);
var authenticateApiV1 = express.Router();
apiV1.use('/auth', authenticateApiV1);
var AuthenticateController = require('./controllers/authenticate');
var ac = new AuthenticateController(authenticateApiV1, passport); //pass in our fully configured passport
//If I call this /login instead of the /auth/login/ in the Controller Class it works!
//app.post("/login",
// passport.authenticate("local-login", { failureRedirect: "/login"}),
// function (req, res) {
// res.redirect("/content");
// });
What is working and what is not working
The Authentication in general is working. In my posted index.js you see app.post("/login", .... If I call this one the authentication is successfully and if I try to reach the restricted content in /auth/content/ req.user has a value (the user object) and I can successfully call req.isAuthenticated() .
BUT, If I use the authentication from /auth/login/username/password the req.user is undefined when trying to reach the restricted Content.
I get no error and the response of /auth/login/username/password/ HTTP Code 301 - 'redirecting to /content.
I have currently no idea what I´m doing wrong here and I´m pretty new to the topic of Node/express/ passport ..
Hope someone has an Idea. If you need something else to help me, just mention it in the comments and I will do my best to provide you everything you need.
Thanks
EDIT:
I recently tried to read the req.user in the login function and even there it is undefined
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
console.log(req.user) //undefined
res.redirect("/content");
}
I guess it could be some async problem and I should use some callback functions, but I don´t know how to apply this in my login()
EDIT 2:
Another Issue I´m facing is the integration of the isLoggedIn() request.
If I do this:
registerRoutes() {
this.router.get('/', this.isLoggedIn, this.getUsers.bind(this));
this.router.get('/:id', this.getSingleUser.bind(this));
}
it results in 401 - Unauthorized
A console.log(req.user); in the isLoggedIn() results in undefined.
But if I call the first route without calling isLoggedIn() and do console.log(req.user); the user object exists.
The correct use of callback with passport authentication for local strategy can be as below:
function(req, res, next){
passport.authenticate('local-login', function(err, user, info){
if(err)
return logger.log('error', err);
if(user)
req.login(user, function(err){
if(err) return next(err);
return res.json({'success': true});
});
if(!user)
return res.json({'error':true, 'message': info.message, 'type': info.type});
})(req, res, next);
}
Please note the use of req.login() to explicitly set user in session.
The only thing I'm finding strange is that your route declarations are different.
In the AuthenticateController the route is declared as:
this.router.post('/login/:username/:password', ...
While in index.js, the route is simply declared as
app.post("/login", ...
How is your client submitting the login credentials to the server? If it is by form, like the tutorial, could it be that having :username and :password declared as route params but being sent by form messes with passport?
Try registering the route exactly like index.js
this.router.post('/login', ...
EDIT:
I've found another dicrepancy. In AuthenticateController the res.redirect("/content"); is not wrapped inside a callback. So it is being executed before Authenticate finishes running.
In the index.js example, passport is being used as a route middleware:
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
res.redirect("/content");
});
While in the passport.js it is inside the callback. Consider declaring it in the route:
registerRoutes() {
this.router.post('/login', this.passport.authenticate("local-login", { failureRedirect: "/login"}), this.login.bind(this));
(...)
}
login(req, res) {
res.redirect("/content");
}
O, better yet, why not use passport's option to declare both success and failure redirects, since that seems to be all that you are doing:
login(req, res) {
this.passport.authenticate("local-login", { successRedirect: "/content", failureRedirect: "/login" });
}
You are passing this.login.bind(this) as a middleware to this.router.post('/login/:username/:password', this.login.bind(this)); but login(req, res) only responds to the request with res.redirect("/content"); i.e. redirecting to /content
So like you said, you need to supply a callback that does something with the user that is returned from passports middleware verify callback.
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
console.log(req.user); // log user in console
res.json({user: req.user}); // send user as json response
});
The custom callback mentioned by #divsingh is if you want to explicitly have control of setting the session, error messages and redirecting the request. Any other information can be found under http://passportjs.org/docs

Ui-router AngularJS: How to access to session variable 'req.user'

I'm using angular to deal with some routes,on the server side I'm using passport so basically I can acess to the user session variable req.user in my views , but when it comes to a route renderred by ui-router my req.user is undefined. Any idea to access to the req.user even it's not an express route
app.js code :
// Express Session
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true
}));
// Passport init
app.use(passport.initialize());
app.use(passport.session());
// Global Vars
app.use(function (req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
res.locals.user = req.user || null;
next();
});
my passport code is as follows :
passport.use('employee',new LocalStrategy(
function(email, password, done) {
Employee.getUserByEmail(email, function(errEmp, emp){
if(errEmp ) throw errEmp;
if(!emp){
return done(null, false, {message: 'Unknown User'});
}
if(emp) {
Employee.comparePassword(password, emp.encryptedpass, function (err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, emp);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
}
});
}
));
router.get('/',ensureAuthenticated ,function(req, res, next) {
res.render('index', { title: 'Nubes' });
});
function ensureAuthenticated(req, res, next){
if(req.isAuthenticated()){
Employee.findById(req.user.id,function (err,emp) {
if(emp) {
res.render('employee/index');
}
})
}
}
router.post('/login', function(req, res, next) {
Employee.findOne({email:req.body.username},function (err,emp) {
if(emp){
passport.authenticate('employee', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
})
});
In my rendered page 'employee/index' I can display my user information but routes that rendered by ui-router don't have a user variable
here is an example code of angular route :
$stateProvider
.state('home',{
url:'/',
templateUrl:'/views/employee/home.html'
})
in home.html user is not defined which is normal because it's not express server who rendred it . what I want is to get this user variable in my ui-router rendered pages
I suggest to implement token based authentication using something like JWT-tokens, you have also passport plugins for those.
If you still want to use sessions based info and pass it to the client, you can store them in some global JS variable, which i highly dont recommend.
here some info about jwt: https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
for the locals if using handlebars it would look something like this:
<script>
var user = {{user}};
</script>
just note that you might need to implement some JSON stringify and decoding in order to get the info as JS object.

passport.authenticate is not a function

Hello I am new in NodeJs and I have been following this tutorial http://code.tutsplus.com/tutorials/authenticating-nodejs-applications-with-passport--cms-21619 to create a app with authenticating.
I tried to follow all the structre and code from the tutorial (code is on github https://github.com/tutsplus/passport-mongo) but when I open my app in browser
i get error this error
TypeError: passport.authenticate is not a function
at module.exports (C:\myApp\routes\index.js:24:34)
This is my index.js route file
var express = require('express');
var router = express.Router();
var passport = require('passport');
var isAuthenticated = function (req, res, next) {
// if user is authenticated in the session, call the next() to call the next request handler
// Passport adds this method to request object. A middleware is allowed to add properties to
// request and response objects
if (req.isAuthenticated())
return next();
// if the user is not authenticated then redirect him to the login page
res.redirect('/');
}
module.exports = function(passport){
/* GET login page. */
router.get('/', function(req, res) {
// Display the Login page with any flash message, if any
res.render('index', { message: req.flash('message') });
});
/* Handle Login POST */
router.post('/login', passport.authenticate('login', {
successRedirect: '/home',
failureRedirect: '/',
failureFlash : true
}));
/* GET Registration Page */
router.get('/signup', function(req, res){
res.render('register',{message: req.flash('message')});
});
/* Handle Registration POST */
router.post('/signup', passport.authenticate('signup', {
successRedirect: '/home',
failureRedirect: '/signup',
failureFlash : true
}));
/* GET Home Page */
router.get('/home', isAuthenticated, function(req, res){
res.render('home', { user: req.user });
});
/* Handle Logout */
router.get('/signout', function(req, res) {
req.logout();
res.redirect('/');
});
return router;
}
Probabbly the problem is there, maybe routing was change in some version of express, but I cant figure out what is the problem.
Can you help pme please ?
I had same problem. Look at app.js. There must be:
var routes = require('./routes/index')(passport);
You have just put the parenthesis at the wrong place.
It should be
router.post('/login', passport.authenticate('login'), {
successRedirect: '/home',
failureRedirect: '/',
failureFlash : true
});

Get POST request when using Router middleware

I'm using Passport to authenticate my users on NodeJS. Currently I'm using ExpressJS and I'm trying to route my traffic. I currently use the following code:
website.js (main file)
require("./routes.js")(app);
routes.js
var pages = {
home: require("./pages/home"),
about: require("./pages/about"),
register: require("./pages/register"),
login: require("./pages/login"),
api: require("./api/index")
};
module.exports = function(app) {
app.use("/", pages['home']);
for (page in pages) {
app.use("/" + page, pages[page]);
}
app.get("/logout", function(req, res) {
req.logout();
req.redirect("/");
});
}
register.js
var express = require('express');
var router = express.Router();
var app = express();
router.get("/", function(req, res) {
res.render("register", { page: "Register", message: req.flash("registerMessage") });
});
app.post("/", passport.authenticate("register", {
successRedirect: "/about/",
failureRedirect: "/register/",
failureFlash: true,
successFlash: "Logged in!"
}));
module.exports = router;
The problem I am facing is that POST requests to this will result in a 404. The page is not found. The GET request (so /register) properly shows the registration form, but upon submitting I get a 404. If I change router.get("/", function(req,res){}) to router.use("/", function(req, res, next) {}), I will get HTTP 500 errors when I call "Next()" (Can't set headers after they are sent.), and POST still doesn't work.
Could anyone tell me how to correctly catch POST requests behind router middleware?
I solved my issue
I solved my issue by using the following:
router.route("/")
.get(function(req, res, next) {
res.render("register", { page: "Register", message: req.flash("registerMessage") });
})
.post(passport.authenticate("register", {
successRedirect: "/about/",
failureRedirect: "/register/",
failureFlash: true,
successFlash: "Logged in!"
}));

Categories

Resources