When using facebook-passport the usual thing to do is to specify the redirect_uri in the constructor of the FacebookStrategy thst you use, something like this:
passport.use("facebook", new FacebookStrategy({
//TODO: Correctly configure me
clientID: "XXXXXXX"
, clientSecret: "XXXXXXXXX"
, callbackURL: "http://localhost:3007/auth/facebook/callback"
},
function(accessToken,refreshToken,profile,done) {
User.findByFacebookId(profile.id, function(err,user) {
if(err){ return done(err);}
if(!user){ return done(null,false)}
return done(null, user);
});
})
);
Then you would set up routes like this:
app.get('/auth/facebook/login', passport.authenticate('facebook') );
app.get('/auth/facebook/login_callback', passport.authenticate('facebook', {
successRedirect:"/login_ok.html"
, failureRedirect:"/login_failed.html"
}
))
Is it possible to change the callback url so that it contains information from parameters passed to the initial login call?
NOTE: This question is more for preserving info that took me a while to work out, to avoid others going down the same paths.
I found the answer using some info found here https://github.com/jaredhanson/passport-facebook/issues/2 and through digging through the way the passport oauth2 component determines callback uris, and information about passport custom callbacks at the bottom of this page http://passportjs.org/guide/authenticate/.
Here's an example that maps calls to /auth/facebook/login/1234 to use the callback /auth/facebook/login_callback/1234
app.get('/auth/facebook/login/:id', function(req,res,next) {
passport.authenticate(
'facebook',
{callbackURL: '/auth/facebook/login_callback/'+req.params.id }
)(req,res,next);
});
app.get('/auth/facebook/login_callback/:id', function(req,res,next) {
passport.authenticate(
'facebook',
{
callbackURL:"/auth/facebook/login_callback/"+req.params.id
, successRedirect:"/login_ok.html"
, failureRedirect:"/login_failed.html"
}
) (req,res,next);
});
#OMGPOP, here you can pass in query params into your callbackUrl.
var Passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
const Router = require("express").Router();
var fbConfig = {
display: "popup",
clientID: "YourFbClientId",
clientSecret: "YourFbClientSecret",
callbackURL: "http://localhost:8686/auth/facebook/callback",
profileFields: ['id', 'name', 'gender', 'displayName', 'photos', 'profileUrl', 'email']
}
Passport.use(new FacebookStrategy(fbConfig,
function(accessToken, refreshToken, profile, callback) {
return callback(null, accessToken);
}
));
Router.get("/auth/facebook", function(req, res, next) {
var callbackURL = fbConfig.callbackURL + "?queryParams=" + req.query.queryParams;
Passport.authenticate("facebook", { scope : ["email"], callbackURL: callbackURL })(req, res, next);
});
Router.get("/auth/facebook/callback", function(req, res, next) {
Passport.authenticate("facebook", {
callbackURL: fbConfig.callbackURL + "?queryParams=" + req.query.queryParams,
failureRedirect: "/login",
session: false
})(req, res, next) },
function(req, res) {
console.log(req.query.queryParams);
//do whatever you want
});
Check out my blog for more information: http://blog.pingzhang.io/javascript/2016/09/22/passport-facebook/
I was struggling to do this specifically with Angularjs, and wanted to redirect back to the same url that the login was initiated from.
My solution was to create a route in Angularjs that just implements a location back. I know this does not specifically answer the question, but I thought it would be helpful for anyone looking to do the same.
On the server:
app.get('/auth/facebook/', passport.authenticate ('facebook'));
app.get('/auth/facebook/callback', function (req, res, next) {
var authenticator = passport.authenticate ('facebook', {
successRedirect: '/fbcallback',
failureRedirect: '/'
});
delete req.session.returnTo;
authenticator (req, res, next);
})
Angular router:
when('/fbcallback', {
template: "",
controller: 'fbCtrl'
}).
Angular controller:
app.controller("fbCtrl", function () {
window.history.back();
});
You could probably do some other client side routing in the controller as well.
Related
This is the google strategy:
passport.use(new GoogleStrategy({
clientID: config.google_consumer_key,
clientSecret: config.google_consumer_secret,
callbackURL: config.google_callback,
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
User.findOrCreate({
email: profile.email,
username:`${profile.given_name} ${profile.family_name}`
}, function (err, user) {
user.connected = true;
user.save();
return done(err, user);
});
}
));
and this is the middleware:
var isAuthenticated = function (req, res, next) {
if(!req.isAuthenticated())
return res.redirect(301,'/login');
next();
};
module.exports = isAuthenticated;
and the route:
router.get('/google-signin',
passport.authenticate('google',
{ scope: [ 'email', 'profile' ] }
));
and the logout
logout = async function(req, res){
req.user.connected = false;
await req.user.save();
req.logout();
return res.redirect('/login');
}
The first time I sign in, there are no problems, but, if I log out and try to sign in again, I'm redirected to /. I can't see the problem.
I've set a breakpoint in the middleware and the first time(when it works) the middleware its executed but , when I logout and try yo login in again the middleware it's not executed
Im using passport to authenticate a user through Facebook.
The successRedirect works great [I'm redirecting into http://localhost:3000/success#_=_], but the failureRedirect doesn't, i'm getting this:
FacebookAuthorizationError: Login Error: There is an error in logging you into this application. Please try again later.
[I'm getting this in my browser-> http://localhost:3000/auth/facebook/callback?error_code=1349003&error_message=Login+Error%3A+There+is+an+error+in+logging+you+into+this+application.+Please+try+again+later.#_=_
Those are my settings:
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function (user, done) {
console.log(user);
done(null, 'this is the user');
})
passport.deserializeUser(function (id, done) {
console.log(id);
done(err, {message: 'this is the user'});
});
router.get('/auth/facebook', passport.authenticate('facebook'));
router.get(
'/auth/facebook/callback',
passport.authenticate('facebook',
{
successRedirect: '/success',
failureRedirect: '/login',
}
),
);
const FacebookStrategy = require('passport-facebook').Strategy;
const facebookStrategy = new FacebookStrategy({
clientID: staretegy.clientId,
clientSecret: staretegy.clientSecret,
callbackURL: staretegy.callbackURL,
profileFields: [
'id',
'first_name',
'middle_name',
'last_name',
],
}, (accessToken, refreshToken, profile, done) => {
done(null, {user: profile});
});
passport.use(facebookStrategy);
As i read in the docs i expected to be redirect to the /login.
/login can be accessed by the browser. (i've also tried to put this full URL path: failureRedirect: http://localhost:3000/login but it won't work, similar URL works with the successRedirect.
This seems an open issue and the main repository barely supported. But you can try to use this fork.
I had a similar issue here and have found a way to handle the error using a middleware error handler (see fbErrorHandler below):
const express = require('express'),
router = express.Router(),
passport = require('passport');
router.get(
'/facebook',
passport.authenticate('facebook')
);
function fbErrorHandler(err, req, res, next) {
// I use flash, but use whatever you want to communicate with end-users:
req.flash('error', 'Error while trying to login via Facebook: ' + err);
res.redirect('/login');
}
router.get('/facebook/callback',
passport.authenticate(
'facebook',
{
failureRedirect: '/login',
failureFlash: true
},
),
fbErrorHandler,
(req, res) => {
// Successful authentication
res.redirect('/authenticated');
}
);
module.exports = router;
I'm trying to implement facebook login with vue-authenticate and passport.
I succeeded in logging into my Facebook account. And i got the 'Callback code' successfully.
This is my callback url
http://localhost:8080/auth/callback?code=AQD0FgQ7I2oci0m3bqOHOBE1EV3Ri1TBnVcqs2PRT8pFNa38NIMX-eYiSr2EiWKQBMoNq1yOeo1QkDG1OiDjF_xUduK-HWMlMazsaBzoGNxiAK3FQH5KQopZ9NUnM2g-UYLpihtpsaFrRVssJkm8Xue1nyKbbWX76EPnPCIEVOfGM_JE4mbENLpp6_w8gwkTS9n8dtsNptDM72UO9zE7mj34J8Yls0A1VqmoZail0J2zwu4hJCzAzbP2FZ531Vo2tCERn2F_4DKsJ-zq_ppZWxRlKuRW9WFBL0UvsuNN_ODiRFs70P3SoK85-xHwzHJvx8VrVxmLlp5x7rVOzy2E2Jma#=
So I used axios to pass the 'Callback code' to the server. because my server code(passport-facebook) is this:
router.route('/auth/facebook/callback').get(passport.authenticate('facebook', {
successRedirect : '/',
failureRedirect : '/'
}));
and axois code in Vue is
this.$http.get('/api/users/auth/facebook/callback',{
params:{
code : this.param
}
})
.then((response) => {
this.movies = param;
})
but it never works.. I don't know why. just wondering, i chaneged axios code to get('api/users/'). and wrote server code like this
router.get('/',()=>{
console.log("good");
});
it works. I can see the 'good' message in console.
Let me know how you implement social sign-in!
if you want to configure Facebook login with passport So you can simply follow these Steps As mention below:
Step 1. Add passport configuration
const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
app.get('/success', (req, res) => res.send("You have successfully logged in"));
app.get('/error', (req, res) => res.send("error logging in"));
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
cb(null, obj);
});
Step 2. Setup facebook configuration
const FacebookStrategy = require('passport-facebook').Strategy;
const FACEBOOK_APP_ID = 'your app id';
const FACEBOOK_APP_SECRET = 'your app secret';
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
return cb(null, profile);
}
));
app.get('/auth/facebook',
passport.authenticate('facebook'));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/error' }),
function(req, res) {
res.redirect('/success');
});
This will fix your issue
Please refer this link https://www.sitepoint.com/passport-authentication-for-nodejs-applications/
I have an express application where I'm using passportjs for managing authentication.
At the moment I only want to support sign in with google in my application. I have implemented this properly and is working fine. Now, I want to restrict access to to the application to only those users who are registered with the application.
Registration is done by admin so there can only be few users who are registered. At the moment, anyone with a google account is able to login to the application. Instead of this, I would like the authentication to happen with google using OAuth2.0 and I would like to check if the user exists in my database before letting the user login to the system.
Update
Following is my code as requested
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var config = require('./config');
var directoryService = require('./services/directory');
/**
* Authentication configuration
*/
module.exports = function (app, router) {
passport.serializeUser(function (user, done) {
//I want to know if this is the correct process
directoryService.findResidentByEmailAddress(user.emails[0].value).then(function (data) {
if (data == null || data.length == 0) {
done({ 'status': 'Invalid Login' });
} else {
done(null, data);
}
});
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
passport.use(new GoogleStrategy({
clientID: config.auth.clientID,
clientSecret: config.auth.clientSecret,
callbackURL: config.auth.callbackURL,
passReqToCallback: true
},
function (request, accessToken, refreshToken, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
}
));
app.use(passport.initialize());
app.use(passport.session());
router.use(function (req, res, next) {
if (req.isAuthenticated() || req.url.startsWith('/auth/')) {
return next();
}
res.redirect('/auth/login.html');
});
router.get('/auth/google',
passport.authenticate('google', { scope: ['email'] }));
router.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function (req, res) {
res.redirect('/');
});
router.get('/logout', function (req, res) {
req.logout();
res.redirect('/auth/login.html');
});
}
I'm having a problem I'm not able to resolve. I'm developing an app with nodejs, using mongodb, expressjs and passportjs as my authentication middleware.
I currently have 3 strategies: facebook, twitter and instagram. What I want to achieve is that when a user login for the first time, if the user is logged with one strategy and logs in with another one save the profiles into the same mongodb user document.
This is my auth/index.js:
require('./local/passport').setup(User, config);
require('./facebook/passport').setup(User, config);
require('./twitter/passport').setup(User, config);
require('./instagram/passport').setup(User, config);
var router = express.Router();
router.use('/local', require('./local'));
router.use('/facebook', require('./facebook'));
router.use('/twitter', require('./twitter'));
router.use('/instagram', require('./instagram'));
And this is, for example, my auth/twitter/index.js
var router = express.Router();
router
.get('/', passport.authenticate('twitter', {
failureRedirect: '/',
session: false
}))
.get('/callback', passport.authenticate('twitter', {
failureRedirect: '/',
session: false
}), auth.setTokenCookie);
module.exports = router;
But how could I pass for example a mongodb _id to this auth/twitter/passport.js in order to pass it to the mongoose query and update an user? Something like making a POST to auth/twitter and accessing to req.user._id ? I can't figure out how to do it.
exports.setup = function (User, config) {
var passport = require('passport');
var TwitterStrategy = require('passport-twitter').Strategy;
var TwitterApi = require('twitter');
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
callbackURL: config.twitter.callbackURL
},
function(token, tokenSecret, profile, done) {
User.findOne({
'twitter.id_str': profile.id
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
user = new User({
role: 'user',
[...]
Thank you very much.
EDIT:
This is how I set my cookie:
function setTokenCookie(req, res) {
if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
var token = signToken(req.user._id, req.user.role);
res.cookie('token', JSON.stringify(token));
res.redirect('/');
}
and the signToken function:
function signToken(id) {
return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*24*30 });
}
Why req.user and req.session are always empty in my Strategy?
EDIT2:
I think I could use the auth.isAuthenticated() function to attach user to the request before invoking the Strategy. What I have done is this:
router
.get('/', auth.isAuthenticated(), passport.authenticate('twitter', auth.isAuthenticated, {
failureRedirect: '/',
session: false
}))
.get('/callback', auth.isAuthenticated(), passport.authenticate('twitter', {
failureRedirect: '/',
session: false
}), auth.setTokenCookie);
But now I'm having this problem:
UnauthorizedError: No Authorization header was found
My request to auth/twitter comes from a $window.location. It seems that this does not attach the user object to the request, because when I make a GET or POST using isAuthenticated() the user object is passed correctly. This is my isAuthenticated() function:
function isAuthenticated() {
return compose()
// Validate jwt
.use(function(req, res, next) {
// allow access_token to be passed through query parameter as well
if(req.query && req.query.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.query.access_token;
}
validateJwt(req, res, next);
})
// Attach user to request
.use(function(req, res, next) {
User.findById(req.user._id, function (err, user) {
if (err) return next(err);
if (!user) return res.send(401);
req.user = user;
next();
});
});
}
FYI I just dealed with this decoding the JWT token in my Strategies. I don't know if this is a good practice but the problem was I was not having my user attached to the request if the request was made with a $window.location.href
So in my strategies I read the cookie and decode it on the fly for searching a user in database.
Thank you.
You can set passReqToCallback to true when you define your passport strategy. It will make the current request, thus the current logged in user available to your callback function.
exports.setup = function (User, config) {
var passport = require('passport');
var TwitterStrategy = require('passport-twitter').Strategy;
var TwitterApi = require('twitter');
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
callbackURL: config.twitter.callbackURL,
passReqToCallback: true
},
function(req, token, tokenSecret, profile, done) {
User.findOne({
'twitter.id_str': profile.id
}, function(err, user) {
if (err) return done(err);
if (!user) {
if (req.user) {
[...]