Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sample of using passport w/ mult strategies
var fs = require("fs")
var ssl_options = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
var port = process.env.PORT || 3000;
var express = require('express');
var ejs = require('ejs');
var passport = require('passport')
, ForceDotComStrategy = require('./lib/passport-forcedotcom').Strategy
, TwitterStrategy = require('passport-twitter').Strategy
, FacebookStrategy = require('passport-facebook').Strategy;
var restProxy = require('./lib/rest-proxy');
//define passport usage
passport.use(new ForceDotComStrategy({
clientID: '[FDCID]',
clientSecret: '[FDCSECRET]',
callbackURL: 'https://127.0.0.1:'+port+'/token'
},
function(token, tokenSecret, profile, done) {
console.log(profile);
return done(null, profile);
}
));
passport.use(new TwitterStrategy({
consumerKey: '[TWITTERID]',
consumerSecret: '[TWITTERSECRET]',
callbackURL: 'https://127.0.0.1:'+port+'/twitter-token' //this will need to be dealt with
}, function(token, tokenSecret, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
}));
passport.use(new FacebookStrategy({
clientID: '[FBID]',
clientSecret: '[FBSECRET]',
callbackURL: 'https://127.0.0.1:'+port+'/facebook-token'
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Facebook profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Facebook account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
}
));
//define REST proxy options based on logged in user
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(null); }
res.redirect('/error')
}
//configure, route and start express
var app = express.createServer(ssl_options);
app.configure(function() {
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'thissecretrocks' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
});
app.set('view engine', 'ejs');
app.set('view options', {
layout: false
});
app.get('/',
function(req, res) {
res.send('Hello World.');
});
app.get('/login', passport.authenticate('forcedotcom'));
app.get('/token', passport.authenticate('forcedotcom', { failureRedirect: '/error' }),
function(req, res){
res.send('Logged In.');
});
app.get('/twitter-login', passport.authenticate('twitter'));
app.get('/twitter-token', passport.authenticate('twitter', { failureRedirect: '/error' }),
function(req, res){
res.send('Logged In.');
});
app.get('/facebook-login', passport.authenticate('facebook'));
app.get('/facebook-token', passport.authenticate('facebook', { failureRedirect: '/error' }),
function(req, res){
res.send('Logged In.');
});
app.get('/error', function(req, res){
res.send('An error has occured.');
});
app.all('/:label/:mode/*',
ensureAuthenticated,
function(req, res) {
console.log(req.session);
if(req.session["passport"]["user"] && req.params.label == "fdc") {
var restOptions = {
useHTTPS : true,
host : req.session["passport"]["user"].instance_url,
headers: {
'Authorization': 'OAuth '+req.session["passport"]["user"].access_token,
'Accept':'application/jsonrequest',
'Cache-Control':'no-cache,no-store,must-revalidate'
}
}
restProxy.proxy(req,res);
}
});
app.get('/*',function(req, res) {
res.render(req.url.substring(1,req.url.length)); //really?
})
app.listen(port, function() {
console.log("Listening on " + port);
});
@jaredhanson

This comment has been minimized.

Copy link

jaredhanson commented Feb 3, 2012

This looks great!

To properly answer your question, it gets a bit into application-level concerns about how you want to allow users to connect accounts from multiple providers.

For something really simple, authentication only needs to be done once (regardless of which provider is used). So you can just put some middleware ahead of the login routes to enforce that.

function ensureUnauthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    // display an "already logged in" message
    return res.redirect('/home');
  }
  next();
}

app.get('/twitter-login', ensureUnauthenticated, passport.authenticate('twitter'));

With that in place, if someone already logged in via Salesforce, they'll just be redirected to /home without affecting the user stored in the session. If you have any UI, you'd want to hide any additional login buttons now that the user is actually logged in. And offer a logout route so they can switch providers.

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

If you want to "link" accounts from multiple providers together, at that point you'll want a database with user records specific to your app, which in turn are associated with third-party account records. Then, in the verify callback, you'll want to perform whatever queries are necessary and supply your app-specific user record to done. That will keep things consistent, so that if someone had linked both Facebook and Twitter, they can return to the app and sign in with either one.

Further discussion about that topic is here: http://passportjs.org/guide/authorize.html

If you go that route (as I do in my apps), I use middleware like this to combine authentication and authorization into a single route:

function authnOrAuthzFacebook(req, res, next) {
  if (!req.isAuthenticated()) {
    passport.authenticate('facebook', { successRedirect: '/settings/accounts',
                                        failureRedirect: '/login' })(req, res, next);
  } else {
    passport.authorize('facebook-authz')(req, res, next);
  }
}

app.get('/facebook-login', authnOrAuthzFacebook);

It seems like in your example, the first approach is the simplest and easiest. But I wanted to mention the other cases in the hopes that it sheds some light on different possibilities.

@joshbirk

This comment has been minimized.

Copy link
Owner Author

joshbirk commented Feb 3, 2012

That makes sense. And I may have been overthinking the original flow as well, I'm now capturing the session as they occur with the token callback and store them out, so:

 passport.authenticate('twitter', { failureRedirect: '/error' }),
  function(req, res){
    req.session["twitter"] = req.session["passport"]["user"];
    res.send('Logged In.');
  });

For instance, to maintain the twitter session. A database would also work, but I think overkill here since I'm essentially only consuming the RESTful responses (and in the long run, Database.com will be the data source). The idea is to display Chatter, Facebook and Twitter feeds on the same page. Let me know if there's something fundamentally flawed there...

Thanks again for the assists - will split the forcedotcom strategy out into a new repo soon.

@jaredhanson

This comment has been minimized.

Copy link

jaredhanson commented Feb 3, 2012

Cool. The session definitely works as a way to store all this state too. It's just a personal preference of mine to not store much in the session, but there's nothing wrong with the approach.

Let me know when the strategy has a repo, and I'll update the site with links to it. Looking forward to it!

@kelonye

This comment has been minimized.

Copy link

kelonye commented Jun 7, 2013

How do you bake identity? email or?

@sonoman

This comment has been minimized.

Copy link

sonoman commented Dec 24, 2013

Hey guys:

I'm starting with passport, and I'm migrating an Auth module I have written in Ruby/Sinatra, where i already support account connection (i'm using omniauth for that).
In omniauth, everything falls in one only route (:provider/callback), and on that route I have the logic to see if the user is already logged in (in which case i connect the accounts), or is a new user.

Now I'm tryinh to underestand your example, and I see like two different providers "twitter" and "twitter-authz", and a different logic for use authenticate and authorize...the question is: Why do I have to use different providers ?

@kevinSuttle

This comment has been minimized.

Copy link

kevinSuttle commented May 13, 2015

Is it possible to append multiple accounts to passport.user when using multiple strategies? For example: passport.user.github, or passport.user.facebook? The goal is to avoid overriding the overlapping values. e.g. When 2 strategies each have an email property, the most recent authenticated Strategy would populate passport.user.email, correct?

I've read http://passportjs.org/guide/authorize/, and dug through the source to find { userProperty: '' }, but that only changes the user property name of passport.user.

Someone on StackOverflow had the same question. All I could do was point him to the docs. https://stackoverflow.com/questions/26453527/using-passport-js-with-multiple-strategies-without-overwriting-user-request-obje/30224234#30224234

@zccmg

This comment has been minimized.

Copy link

zccmg commented May 11, 2016

I have App that authenticate with Saml 2.0 strategy, only authentication so I need to do authorization strategy, using OAuth strategy.
and I have multiple authorization requests depending on the resource requesting, like when the user request /products send authorization request (if not authenticated before) only for this resource, and another request to another resource, ... ...
so I thing is a very help fully if Passport js supports a multiple strategies by separating their information in the session, like saying req.resources.products.access_token and req.user.[email|name]

@hiteshsethiya

This comment has been minimized.

Copy link

hiteshsethiya commented Sep 7, 2016

I'm new to passportjs, Is there a strategy which keeps only one active session per user per device?
Say user A logs into device D1 and then logs into device D2, then the session in device D1 should be invalidated. How can i do that?
Thank you

@alexkaralanian

This comment has been minimized.

Copy link

alexkaralanian commented Aug 24, 2017

Hi all been using passport for a while - is there a way to implement JWT token authentication while connecting with all these providers - ie sending ONE master token to the client that protects the API routes, and grants further access to the google, facebook, etc... tokens in the DB which can be queries and used to make further downstream requests to API data.

Im looking to build an app that connects several API providers and allows me to query their data but doesn not use session -- and I want to use one JWT token on the client side. Is this doable?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.