Query the db from Angular to Nodejs with passportjs - angularjs

I am able to authenticate with passport-facebook and store the userid and username from facebook to my db. I am querying nodejs from angular controller :
MyApp.controller("FacebookLogin", function($scope, $http){
$scope.login = function(){
$http.get("/facebooklogin").then(function(response){
if(response.data){
console.log(response.data); // nothing is logged here
} else {
console.log("No Data");
}
});
}
});
In node :
app.get('/facebooklogin', passport.authenticate('facebook'));
app.get('/auth/facebook/callback', passport.authenticate('facebook', function(err, user, info){
console.log(err, user, info);
if(err) throw err;
// res.send(user); //Do I send the user info from here?
}));
I want to send the userid to angular to store in localStorage and use that to query the db for future requests. Is that the proper way to do it? It's a SPA. I see a connect.sid cookie is created in my browser. How do i extract the userid from it if it has the userid or what do i send in my post/get that nodejs recognizes thru the session?
I did read but did not understand what these two things do exactly :
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
Many Thanks.

The facebook Auth mechanism (oAuth) is asynchronous, and so it works differently to what you are expecting.
You don't do a $http.get("/facebooklogin"). Rather you should redirect your browser to '/facebooklogin'
The way the oauth process works in a nutshell is
User clicks a 'Login with facebook' button, which should do a hard redirect to 'http://youserver/facebooklogin' (you can also do fancy stuff with iframes, dialogs, etc.)
The server side call app.get('/facebooklogin', passport.authenticate('facebook')); will respond with a redirect to facebook.com passing through your apps credentials. At this stage, the user has effectively left your application. Facebook handles the login for the user so that you never get to see their username/password etc.
If successful, facebook will redirect the browser back to your callback URL, passing through a session id. This is received at http://yourserver/facebook/callback. The server side code would then typically store this in the user session in some way, ie. in the DB, a cookie, or just redirect to a page in your app that can receive the session info and save it in local storage.

Related

Ionic + Passport isAuthenticated() returns false

I have my app in angularJs 1.6 built with MEAN stack working well, i'm using passport for authentication.
When I decide to test it with ionic, the app itself works well (files are basically identical) but the authentication with passport is broken
I can register and login a user, but when I want to check if the user is logged by using req.isAuthenticated() on my server, it always answers false.
I think it's because when I make a request from my normal angular app, the request contains a user object with password and email, but when I do from my ionic app, the user is missing
I've spend the day working on it, any help would be great !
EDIT 1:
Sorry for not including code, it's my first experience here
My login route + my function for login
app.post('/api/login', login);
function login(req, res, next) {
//console.log(req);
passport.authenticate('local-login', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting signup
if (! user) {
return res.send({success : 'false', message : req.flash('loginMessage') });
}
req.login(user, function(err) {
if (err) { return next(err); }
//console.log(req);
return res.send({success : 'true', message : req.flash('loginMessage') });
});
})(req, res, next);
}
The problem is, req.login is executed and I get a success: true, but with the ionic/cordova app, nothing seems to be memorized
After that, when I try to check if the user is logged with this
app.get('/api/login/loggedin', function(req, res) {
res.send(req.isAuthenticated() ? req.user : '0');
});
I always get '0', I think it is because cordova/ionic app cannot use cookies (the difference between requests is also the lack of cookie from the ionic one), but I can't understand how to manage a solution that works both with my web angular app and it's ionic version (still with passport)
Solution I just found:
In fact, it was a CORS problem because I don't know exactly why but Ionic/cordova didn't put {user:...} informations in the post request
Simply add
var cors = require('cors');
app.use(cors({origin: 'http://localhost:8100', credentials: true}));
to your server, it allows req to contains informations needed
and add
{withCredentials: true}
to all of your requests that are going to be checked with isAuthenticated(). For example:
$http.get('http://localhost:8081/api/todos', {withCredentials: true});
So the request sent contains the {user:...} part
I don't exactly know why you need to authorize it both in client and server side but it works fine

How to auto login in angularjs application with url

Can anyone say me how to auto login with angularjs 1.x .I will be sending the email with url and when they click on that url it should autoredirect to home page.
The url will be having email and encrypted password:
http://localhost:8080/login/amg1.2.3#outlook.com/$2a$08$cZPWmzta7Gn9Mj14r7zGWeMPKNKkkg8JS3gWNL2fQaFuBwrhgKQC
Clicking on this url should redirect me to home page without any login page.
I am using passport for login functionality.
can you explain me how,iam using passport with form post like this
app.get('/login/:email/:pwd', function(req, res) {
console.log(req.params.email);
console.log(req.params.pwd);
var request = require('request');
request.post({
headers: {'content-type' : 'application/x-www-form-urlencoded'},
url: 'http://localhost:8080/login/',
form: { email: req.params.email,password:req.params.pwd }
}, function(error, response, body){
console.log(error);
console.log(body);
console.log(response);
});
});
Please check the gist how I am doing and tell me how can i improve the code gist.github.com/agupta330/0fb55b50eecf4b1305ebfe15869b295d
It has nothing to do with angular, because it is serverside.
You should not use the encryted password, because your link will never expire. It is better to generate a random key and store it in your database like you do it with sessions ids.
If the user clicks on the link an the key is valid and not expires, you generate a new session and send the session cookie together with the redict to your angular page to the user.
For the angular page you not have to change anything, because it seems like the user is loggedin like normal (with the session in the cookie)

User not recognized by Express Passport with my strategy

I have a strange behavior with my Angular - Express + Passport behavior.
On my application, a user is authenticated this way: the AngularJS application calls an external service, linked to NTML, which returns several information such as the email of the current user.
Then, the AngularJS application calls a REST service on my Express backend (/rest/userProfile) to get the full profile of the user. That's when I authenticate the user on the back-end.
However, at the same time, the AngularJS application (during its initialization) calls several others REST api on my back-end (such as retrieving the application configuration in /rest/config, some other stuffs to display on the homepage). These calls are not protected and are generally handled before the call to /rest/userProfile.
In others words, the Express session is created by the first request received on the back-end, which is generally /rest/config and not /rest/userProfile (I think this point is really important).
After all that initialization, the user can call some protected REST api, like /rest/foo/.
But it appears that sometimes, the user is then not recognized on the back-end, despite the fact that the request contains the Express cookie (connect.sid). The logs show that the request object does not contain the passport property.
If I refresh one or two times the browser, everything start to work correctly, i.e. the user is finally recognized. After that, during some times, I can restart my browser and it will work directly without problem.
It seems that if I delay all the requests (like /rest/config) to ensure that /rest/userProfile is the first request received on the server, everything works correctly (i.e. the user will be recognized correctly on protected routes).
Is my understanding correct, in the sense that with my current implementation I have to make the call to /rest/userProfile be the first request received on the backend, so the session created by Passport will contains the user information?
If yes, what I have to change in my implementation (cf. below) to make it work, regardless when the call to /rest/userProfile is received (of course before any protected route)?
If no, what is wrong on my site? Any clue?
The code
On my index.js, I set the following Express middlewares:
app.use(session({
secret: 'xxx',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
On my main route JS file:
passport.serializeUser(function(user, done) {
console.log('[serialize user]', user);
done(null, user.username);
});
passport.deserializeUser(function(username, done) {
console.log('[deserialize username]', username);
User.findOne({username: username}, function(err, user) {
done(err, user);
});
});
passport.use('ntml-strategy', new LocalStrategy({
usernameField: 'mail',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
console.log('[ntml strategy]', username);
User.findOne({username: username}, function(err, user) {
if (err) {
return done(err);
} else if (user && user !== null) {
return done(null, user);
}
// User does not exist in DB.
// We should create a new user with data received from client.
var newUser = new User({
username: username,
roles: ['user']
});
User.create(newUser, function(err, userSaved) {
if (err) {
return done(err);
}
return done(null, userSaved);
});
});
}));
// The REST api to get user profile
app.post('/rest/userProfile', passport.authenticate('ntml-strategy'), getUserProfile);
function getUserProfile(req, res) {
var user = req.user;
console.log('[getUserProfile]', user);
if (!user) {
return res.status(401).end();
}
res.json({
username: user.username,
roles: user.roles
});
}
// In other file, another route:
app.get('/rest/foo/', isAuthenticated, getUserStuff);
function isAuthenticated(req, res, next) {
if (!req.isAuthenticated()) {
return res.status(401).end();
}
next();
}
function getUserStuff(req, res, next) {
// Do stuff
}
On the User model, I've set the passport-local-mongoose:
User.plugin(passportLocalMongoose, {
usernameField: 'username',
usernameUnique: true
});
Note that removing that plugin does not change anything in the behavior.
Thanks.
For versions: node 5.5, express 4.10.8, passport 0.3.2, passport-local 1.0.0 and Angular 1.5.

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Get email ID/username after login using Google+ API

I want to access the user_name/email_id of the user who logs onto my website using Google+ API. So far I have implemented the Google+ API and the return value is:
User Logged In This is his auth tokenya29.AHES6ZRWhuwSAFjsK9jYQ2ZA73jw9Yy_O2zKjmzxXOI8tT6Y
How can I use this to get the username/email id?
Specifically for retrieving an email address of an authenticated user, keep in mind that you will need to include the userinfo.email scope and make a call to the tokeninfo endpoint. For more information on this, see https://developers.google.com/+/api/oauth#scopes.
if you are correctly logged in, it's enough to call the Google+ api at this URL:
GET https://www.googleapis.com/plus/v1/people/me
where the userId has the special value me, to get all the information about the logged user. For more information see:
https://developers.google.com/+/api/latest/people/get
I'm adding a code sample to help others.
In this case the login operation is performed against Google requesting email, as well as user profile information like name,.... Once all this information is retrieved, a request to my own login service is performed:
function OnGoogle_Login(authResult) {
if (authResult['access_token']) {
gapi.client.load('oauth2', 'v2', function()
{
gapi.client.oauth2.userinfo.get().execute(function(userData)
{
$("#frmLoginGoogle input[name='id']").val(userData.id);
$("#frmLoginGoogle input[name='name']").val(userData.name);
$("#frmLoginGoogle input[name='email']").val(userData.email);
$.ajaxSetup({cache: false});
$("#frmLoginGoogle").submit();
});
});
}
}
$(document).ready(function() {
/** GOOGLE API INITIALIZATION **/
$.ajaxSetup({cache: true});
$.getScript("https://apis.google.com/js/client:platform.js", function() {
$('#btnLoginGoogle').removeAttr('disabled');
});
$("#btnLoginGoogle").click(function() {
gapi.auth.signIn({
'callback': OnGoogle_Login,
'approvalprompt': 'force',
'clientid': 'XXXXX.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
'requestvisibleactions': '',
'cookiepolicy': 'single_host_origin'
});
});
});

Resources