Login via passport facebook strategy with angular ui router - angularjs

I have implemented client side routing using 'Angular UI router'. I am using passport facebook strategy at the server side to handle facebook based authenticatoin.
I have my angular routes as follows:
angular.module('homepage').
config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
// INDEX STATES ========================================
.state('index', {
url: '/',
templateUrl: '../../html/partials/partial-index.html'
})
// HOME PAGE AND MULTIPLE NAMED VIEWS =================================
.state('home', {
url: '/home',
templateUrl: '../../html/partials/partial-home.html'
});
});
Now i initiate the authentication on click to following link:
<a class="clickable" href="/auth/facebook">Log In</a>
Following are my server side routes:
// route for facebook authentication and login
app.get('/auth/facebook',
passport.authenticate('facebook', {
scope : 'email'
}));
// handle the callback after facebook has authenticated the user
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect : '/success',
failureRedirect : '/login'
}));
app.get('/success', isLoggedIn, function(req, res) {
// client side redirection without passing user info
res.redirect('/#/home');
});
On authentication success, my page reloads and loads 'localhost:8888/#/home' and loads the 'home' route state. But the issue is that i have not recieved any user info at the client side after redirection happened.
Ideally i would like to do something as follows:
app.get('/success', isLoggedIn, function(req, res) {
// what i would like to do ideally
res.json({
path: '/#/home',
user : req.user // get the user out of session and pass to template
});
});
But I dont know how to pass this info to angularjs current page context from nodejs routes. How can i implement this.
What is the good approach to authenticate via facebook strategy and then do angular route redirection while passing user info to angular.

You should write a custom response handler for passport when you use a strategy. Like this:
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ facebookId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
Are you wrote that?
Don't forget about user serialization:
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});

Related

callback missmatch error in angular with auth0

Hi I'm using Auth0 with Nodejs and angularjs
here is what i want to achieve
1. I want to user to signup using auth0's lock
2. as soon as user logs in a callback should be called at my nodejs server
3. after that i will get the user information and user's JWT
4. then i will redirect user to dashboard page and store the JWT in browser
What's the problem with Auth0's example
1. they provide example either for angular or nodejs standalone not the combined
2. there is combined(client server) example but that's using jade with nodejs
my code snipped
Angular snipped
var options = { auth: {
redirectUrl: 'http://localhost:3000/callback'
, responseType: 'code'
, params: {
scope: 'openid name email picture'
}
}
}
lockProvider.init({
clientID: 'cUlBNhhaIblahBlahRp6Km',
domain: 'rishabh.auth0.com',
option:options
});
node snipped
router.get('/callback',
passport.authenticate('auth0', { failureRedirect: '/url-if-something-fails' }),
function(req, res) {
console.log(req.user);
res.json({id_token:req.user});
});
Note: I've added this callbacks in auth0
http://localhost:3000/callback
but dont know why I'm facing this error for callback error when I've mentioned my redirect URL in angular side
can anyone tell me what is the problem with my code why auth0 not redirecting me to this url http://localhost:3000/callback
and the interesting thing is when i use simple lock.js instead of angular like this
<script>
var options = { auth: {
redirectUrl: 'http://localhost:3000/callback'
, responseType: 'code'
, params: {
scope: 'openid name email picture'
}
}
}
var lock = new Auth0Lock('clientID', 'rishabh.auth0.com',options);
lock.show();
</script>
then in this case my nodejs /callback route is called properly, so what I'm doing wrong with angular ?
please help
Update
this is my project structure
full code
https://github.com/LabN36/error
Config.js
var Auth0Strategy = require('passport-auth0');
var passport = require('passport');
var strategy = new Auth0Strategy({
domain: process.env.AUTH0_DOMAIN || 'rishabh.auth0.com',
clientID: process.env.AUTH0_CLIENT_ID || 'cUheWwRxm7OLdHBRzlBNvfvfvfvfvhhaI1lxRp6Km',
clientSecret: process.env.AUTH0_CLIENT_SECRET || 'e37eIZpjgBnDMBtrYMwvffvfvfvfaU4jSqt8qylZMT9Oj1EiffLGViinWQ5AiuWi1-WBwA8v3',
callbackURL: process.env.AUTH0_CALLBACK_URL || 'http://localhost:3000/callback'
}, function(accessToken, refreshToken, extraParams, profile, done) {
// accessToken is the token to call Auth0 API (not needed in the most cases)
// extraParams.id_token has the JSON Web Token
// profile has all the information from the user
console.log(extraParams.id_token);
//save user detail with token here and return token only profile
return done(null, extraParams.id_token);
});
passport.use(strategy);
// you can use this section to keep a smaller payload
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
module.exports = passport;
AngularApp.js
angular.module('workApp',['auth0.lock'])
.config(function($locationProvider,lockProvider){
var options = { auth: {
// redirect:true,
responseType: 'code',
redirectUrl: 'http://localhost:3000/callback',
params: {
scope: 'openid name email picture'
}
}
}
lockProvider.init({clientID: 'cUheWwRxm7OLdHBRzlBNhhaI1lxRp6Km',domain: 'rishabh.auth0.com',
option:options
});
$locationProvider.html5Mode(true);
})
.controller('homeCtrl',function($scope,$http,$location,$window,lock){
$scope.login = function() {
// window.alert("magic")
console.log("Messed Up really")
var vm = this;
vm.lock = lock;
lock.show();
}
}).run(function(lock){
lock.interceptHash();
lock.on('authenticated', function(authResult) {
localStorage.setItem('id_token', authResult.idToken);
lock.getProfile(authResult.idToken, function(error, profile) {
if (error) {
console.log(error);
}
localStorage.setItem('profile', JSON.stringify(profile));
});
});
})
According to the screenshot the error happens because the authentication request is made with a redirect_uri of:
http://localhost:3000/
and the allowed callback URL's are:
http://localhost:3000/callback
http://35.162.118.253:3000/callback
Also based on the code you shared you're indeed setting the redirectUrl to be http://localhost:3000/callback so there may be something on the rest of the code that either causes that value to be overridden or not used at all.
If the redirectUrl is not set, Lock will use the current page so the likely culprit is that the options you set are not being used. If you still don't find the cause for this, update the question with the code associated with how Lock is shown.
Damn, the actual root cause was already shown in the code you initially provided, but only looking now at the full code made it possible for me to catch it...
You're calling lockProvider.init() with:
{ clientID: [?], domain: [?], option: options }
when it should be called with:
{ clientID: [?], domain: [?], options: options } // options instead of option

Access linkedin info not working using PassportJS + NodeJs + Angular

I am implementing Passportjs w/ node in my Angular application to retrieve the users linkedIn info such as name, current positions, etc... here's the scenario.
User NOT already logged into LinkedIn -> redirects user to linkedIn sign in page. (Great!)
User already logged into LinkedIn -> redirects to page saying "LinkedIn Verification Failed" (WTF?)
Code:
Upon button click in my Angular controller:
$scope.verifyLinkedIn = function(){
$window.location.href = '/auth/linkedin' ;
};
Then this is what my node routes.js file looks like for the linkedIn Passport:
{
path: '/auth/linkedin',
httpMethod: 'GET',
middleware: [passport.authenticate('linkedin', {
successRedirect: '/edit_roommate',
failureRedirect: '/edit_roommate'
})]
},
{
path: '/auth/linkedin/callback',
httpMethod: 'GET',
middleware: [function (req, res) {
console.log('/auth/linkedin/callback - req.url: ' + req.url);
AuthCtrl.loginWithLinkedIn(req, res, function(err, res)
{
console.log('done!');
//res.render('search');
});
}]
},
From this point on the page redirects to:
http://www.apartmate.com/auth/linkedin/callback?code=AQQ9UPfnVSTlaHv8OYzPdnIcIt0KtpGW7OCbv3ngolCoR3mWGNADymFwGckWEU7RB1VMthn77ILPaxHtlO3N9Kx-NghPtGbGrIYxkyAzZzyNdZM-Fv8&state=vSMzPEsAQhviuHqgF8xEGhfQ
With the page saying "LinkedIn Verification Failed" and the console wiped clean. Am I missing something? Is my implementation incorrect? Any help would be much appreciated. Thanks.

redirect to another page rather than index after sign in via passport in MEAN.js

I am using MEAN.js to create a CRUD web application.
in this app I use passport for Authentication
It redirect to the index page after signing in
But how can I redirect to another page like my module page?
Cheers!
You need to use authenticate(http://passportjs.org/docs/authenticate)
Use it as a route handler
app.post('/login', passport.authenticate('facebook', function(error, user, info){
if (error) {
log.error(error);
next(error);
} else {
if (user) {
// do the redirect here
} else {
next(new Error("Invalid Request"));
}
}
}));
Or as a middleware
app.post('/login',
passport.authenticate('facebook'),
function(req, res) {
// do redirect here
});
Or use options
app.post('/login',
passport.authenticate('facebook', { successRedirect: '/user',
failureRedirect: '/login' }));

Angular.js: page flickering on authentication redirect

I'm implementing some simple client-side authentication logic in Angular.js. The pages involved are:
/account#/login (public)
/account (require login)
/account#/settings (require login)
When a user is not logged in and try to visit either /account or /account/#/settings, the app is supposed to redirect to the login page.
I have the following routes configured using ui-router:
$stateProvider
.state('overview', {
url: '/',
restricted: true
})
.state('settings', {
url: '/settings',
restricted: true
})
.state('login', {
url: '/login',
restricted: false
})
and upon URL change, I check if the upcoming page is a restricted page and whether the current user is not logged in. If so redirect to login.
app.run(function($rootScope, $location, $state, auth) {
$rootScope.$on('$stateChangeStart', function(event, next) {
if (next.restricted && !auth.isLoggedIn()) {
event.preventDefault();
$state.go('login');
}
});
});
auth is just a service that checks the login status and returns either true (logged in) or false (not logged in).
Here's my question:
Even though this (kind of) works, I see a page flickering issue when trying to visit a restricted page while not logged in. The page flashes the contents of the restricted page quickly before redirecting me to the login page.
I did a little bit researching online and some people have mentioned the potential solution could be using resolve when defining my states, since the page won't load unless it resolves successfully. However, when I try to add
resolve: {
load: function(auth) {
return auth.isLoggedIn();
}
}
It didn't work. What am I missing? Is using resolve the way to go?
The way you are currently doing it will check if the user is logged in or not and set load to true or false. Also controller gets instantiated before load is resolved which is why you see the flickering. You need to achieve two things here:
Make sure that load is resolved before the controller is instantiated.
If user is not logged in, redirect the user to the login page.
For the first part we need to use a promise as it will be resolved and converted to value before controller is instantiated. This is what the documentation says:
If any of these dependencies are promises, they will be resolved and
converted to a value before the controller is instantiated and the
$stateChangeSuccess event is fired.
Following code can do that for us:
var isLoggedin = ['auth', '$q',
function(auth, $q) {
var deferred = $q.defer();
//assuming auth.isLoggedIn returns a promise
var loginPromise = auth.isLoggedIn();
loginPromise.then(
function(response) {
deferred.resolve(response);
},
function(error) {
deferred.reject('Not logged in');
});
return deferred.promise;
}
];
And states will use isLoggedin:
$stateProvider
.state('overview', {
url: '/',
resolve: {
loggedin: isLoggedin
}
})
.state('settings', {
url: '/settings',
resolve: {
loggedin: isLoggedin
}
})
.state('login', {
url: '/login'
})
For the second problem, that is redirecting the user to login page, you can listen to $stateChangeError event which is fired in case the state is not resolved, and use $state.go to redirect the user.

Authentication when using angularjs and passportjs

I am currently using passportjs for authenticaton.
I have come across a stage where i need to ensure the user is authenticated if the url is typed in the browser/ I have been using the passportja example which has the following:
app.get('/admin', ensureAuthenticated, function(req, res){
console.log('get admin');
res.render('admin', { user: req.user });
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
I am using angularjs for routing so my get does not work and run the ensure authenticated.
How should this be implemented?
With AngularJS, you need to restrict the display of templates to the user.
So, let us say you have the following code in AngularJS:
$routeProvider.when('/admin', {
templateUrl: '/partials/admin-page.html'
});
When the user tries the /admin route, AngularJS will then request the template /partials/admin-page.html.
Thus, in your nodeJs server, you then implement the following code:
app.get('/partials/admin-page.html', ensureAuthenticated, function (req, res) {
res.render('admin', { user: req.user});
});

Resources