Ideally, there is a generic sign in button on the homepage, which redirects to several sign in buttons, one of which is for Facebook. The user clicks that button and, if authenticated, is redirected to /my-reptiles/.
However, what actually happens is that when the user clicks "Log in with Facebook", they are not prompted with a confirmation from Facebook and are immediately redirected back to the homepage from the ensureAuthenticated() method in my-reptiles.js. When running in private browsing, I am asked to log in to Facebook before being redirected, but I am not asked to approve use of Facebook as a login method. Is this normal?
Also a local mongodb entry for the user is still created even though isAuthenticated() fails.
On the Facebook developer console I have app domains set to localhost and site URL set to http://localhost:3000/.
This is the console output:
GET /auth/facebook 302 4.309 ms - 0
GET /auth/facebook/callback?code=LONG_CODE_HERE 302 215.462 ms - 68
Unauthenticated request!
GET /my-reptiles 302 0.879 ms - 46
GET / 304 14.854 ms - -
This is the network log:
my-reptiles.js
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
console.log("Unauthenticated request!");
res.redirect('/');
}
}
router.get('/', ensureAuthenticated, function(req, res, next) {
//Do stuff
});
module.exports = router;
app.js
//Requires here
function findOrCreate(key, cb) {
db.collection('users').findOne(key, (err, user) => {
if (err) {
console.error(err);
} else {
if (!user) {
db.collection("users").insert(key, cb)
} else {
cb(err, user);
}
db.close();
}
});
}
// config
passport.use(new FacebookStrategy(
{
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL
},
function(accessToken, refreshToken, profile, done) {
findOrCreate({
auth: "facebook",
id: profile.id
}, function (err, user) {
return done(err, user);
});
}
));
var app = express();
//View engine setup here
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({
extended: false
}));
app.use(cookieParser('sekret'));
app.use(express.static(path.join(__dirname, 'public')));
//Routes here
const cookieExpirationDate = new Date();
const cookieExpirationDays = 365;
cookieExpirationDate.setDate(cookieExpirationDate.getDate() + cookieExpirationDays);
app.use(session({
secret: 'sekret',
saveUninitialized: true,
resave: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.get('/auth/facebook', passport.authenticate('facebook'));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect: '/my-reptiles',
failureRedirect: '/'
})
);
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
//Error handling here
module.exports = app;
The problem was that I was adding session and passport after my routes, when it should have been before them, as per this answer.
//Routes WERE here
const cookieExpirationDate = new Date();
const cookieExpirationDays = 365;
cookieExpirationDate.setDate(cookieExpirationDate.getDate() + cookieExpirationDays);
app.use(session({
secret: 'sekret',
saveUninitialized: true,
resave: true
}));
app.use(passport.initialize());
app.use(passport.session());
//Routes SHOULD BE here
Related
I'm writing one of my first applications in NodeJS so please bear with me. I've managed to successfully authenticate a user to our Active directory and I can see the connect.sid cookie being set and used on the subsequent requests.
Upon debugging the application by dumping the req object I can also see that the user variable has been set successfully. From the documentation I've read that seems to be a criteria for a successful session match?
However, the request is still getting a 401 Unauthorized.
To summarize:
The user is successfully authenticated after posting credentials /login.
Upon successful authentication the user is redirected to "/".
The "/" path replies with 401 Unauthorized.
Any ideas much appreciated. Code below.
const express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var passport = require('passport')
var ActiveDirectoryStrategy = require('passport-activedirectory')
// Setup the authentication strategy
passport.use(new ActiveDirectoryStrategy({
integrated: false,
ldap: {
url: 'ldap://myad.company.com',
baseDN: 'DC=domain,DC=company,DC=com',
username: 'user',
password: 'password'
}
}, function (profile, ad, done) {
ad.isUserMemberOf(profile._json.dn, 'Group', function (err, isMember) {
if (err) return done(err)
return done(null, profile)
})
}));
passport.serializeUser(function(user, done) {
done(null, JSON.stringify(user));
});
passport.deserializeUser(function(user, done) {
done(null, JSON.parse(user));
});
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(session(
{ secret: "password" }
));
app.use(passport.initialize());
app.use(passport.session());
// For debugging purposes
app.use(function (req, res, next) {
console.log(req)
next()
})
// The login page posts a form containing user and password
app.get("/login", (req, res) => {
res.sendFile(__dirname + '/public/index.html');
})
// Handler for the login page. Receives user and password and redirects the user to /
app.post('/login',
passport.authenticate('ActiveDirectory', {
failWithError: true,
successRedirect: "/",
failureRedirect: "/login"
}
), function(req, res) {
res.json(req.user)
}, function (err) {
res.status(401).send('Not Authenticated')
}
)
// This is where the issue happens. The page returns "Unauthorized".
// Using console.log(req) shows that the user property has been set to the req object.
// However, for some reason it still fails.
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Found what I did wrong!
The .authenticate method is only used to validate credentials, not to validate a session.
So this:
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Should become:
app.get('/', function(req, res, next) {
// This is verifying that the user part has been populated,
// which means that the user has been authenticated.
if (req.user) {
res.send('Returning with some text');
} else {
// If the user property does no exist, redirect to /login
res.redirect('/login');
}
});
Another thing that I changed was the serialize/deserialize functions:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
This removes redundant serializing/deserializing.
These articles really helped me understand the flow:
http://toon.io/understanding-passportjs-authentication-flow/
https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive
Hope it helps someone else!
/Patrik
I am trying to use passportjs with the following code.
So when the user goes to http://localhost:3000, he should automatically be redirected to /hello but as it does, it redirects to /hello?failure.
I have tried to debug, look around but haven't found the issue and the solution.
const express = require('express');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const session = require('express-session');
let app = express();
app.use(session({
secret: 'mysecret',
cookie: {
secure: false
},
resave: true,
saveUninitialized: true
}
));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
passport.use('local-login', new LocalStrategy(function (username, password, done) {
console.log(username + ' username ');
console.log(password + ' password ');
return done(null, { name: 'suhail' });
}));
passport.serializeUser(function (user, done) {
console.log(user, ' serialize ');
done(null, user.name);
});
passport.deserializeUser(function (id, done) {
console.log(id, ' deserialize ');
done(null, { name: 'suhail' });
});
app.get('/', passport.authenticate('local-login', { successRedirect: '/hello', failureRedirect: '/hello?failure' }));
app.get('/hello', (req, resp, next) => resp.send('hello').end());
app.listen(3000);
What is it that I am missing? It should go to http://localhost:3000/hello as the middleware always resolves.
Note:
- None of the middleware defined is called. It just redirects to /hello?failure when a GET request to / is made.
You are not providing username and password in your get request. Therefore the Strategycallback is never called.
Try adding this middleware in front of your routing to see what i mean:
app.use((req, res, next) => {
// fake body content for authentication
req.body = {username: 'devil', password: '666'}
next()
})
app.get('/', passport.authenticate('local-login', { successRedirect: '/hello', failureRedirect: '/hello?failure' }));
I am trying to integrate passport into my node.js app.
app.js file
const app = express();
app.set('view engine', 'pug');
app.use('/libs', express.static('node_modules'));
require('../config/auth.config')(app, data, passport);
app.use((req, res, next) => {
res.locals.user = req.user;
next();
});
app.get('/', (req, res) => {
// those objects are populated correctly after redirect from auth middleware
console.log(req.session)
console.log(req.user)
return res.render('home');
});
app.get('/login', console.log(req.user);
// req.user is undefined here
if (req.user) {
return res.redirect('/');
}
return res.render('login'););
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
}));
auth.config.js
const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const LocalStrategy = require('passport-local');
const MongoStore = require('connect-mongo')(session);
const config = require('./config');
const configAuth = (app, {
users
}, passport, db) => {
app.use(cookieParser('Purple Unicorn'));
app.use(bodyParser.urlencoded({
extended: true,
}));
app.use(bodyParser.json());
app.use(session({
store: new MongoStore({
url: config.connectionString
}),
secret: 'Purple Unicorn',
resave: true,
saveUninitialized: true,
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy((username, password, done) => {
return users.login(username, password)
.then((user) => {
if (user) {
return done(null, user);
}
return done(null, false);
});
}));
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
users.getUserById(id)
.then((user) => {
console.log(user);
if (user) {
done(null, user);
}
done(null, false);
});
});
app.use((req, res, next) => {
res.locals = {
user: req.user,
};
next();
});
};
module.exports = configAuth;
The data object is working correctly.
After the post request on /login with correct data, I am redirected to / where console.log(req.user) prints the correct user. It is also added in the req.session object.
After I follow a link to /login, it should redirect me after the check for req.user but returns undefined. Sessions in mongo are stored correctly.
It seems passport is not saving the session correctly.
The problem is in your deserializeUser method where you always run done callback twice. In if statement you should use return done(null, user); to get out from function;
I am trying to do salseforce aouth in nodejs using passportjs butt getting error.
Cannot GET /callback?code=aPrxaSyVmC8fBbcSNDQ8G8UtoR.YZip2hdfAGpMSc2hf0798gD_UoDle1dqqC0HnPyewycgKMw%3D%3D
Here is my code
'use strict';
var express = require('express'),
passport = require('passport'),
util = require('util'),
ForceDotComStrategy = require('passport-forcedotcom').Strategy;
//----------------------------------------------------------------------------
// REPLACE THE BELOW SETTING TO MATCH YOUR SALESFORCE CONNECTED-APP'S SETTINGS
//----------------------------------------------------------------------------
var CF_CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
var CF_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxx';
var CF_CALLBACK_URL = 'http://localhost:3000/callback';
var SF_AUTHORIZE_URL = 'https://login.salesforce.com/services/oauth2/authorize';
var SF_TOKEN_URL = 'https://login.salesforce.com/services/oauth2/token';
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
var sfStrategy = new ForceDotComStrategy({
clientID: CF_CLIENT_ID,
clientSecret: CF_CLIENT_SECRET,
callbackURL: CF_CALLBACK_URL,
authorizationURL: SF_AUTHORIZE_URL,
tokenURL: SF_TOKEN_URL
}, function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
delete profile._raw;
return done(null, profile);
});
});
passport.use(sfStrategy);
var app = express();
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
secret: 'keyboard cat'
}));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res) {
console.log(req.user);
if(!req.user) {
req.session.destroy();
req.logout();
return res.redirect('/login');
}
res.render('index', {
user: req.user
});
});
app.get('/login', function(req, res) {
req.logout();
req.session.destroy();
res.render('login', {
user: req.user
});
});
app.get('/auth/forcedotcom', passport.authenticate('forcedotcom'), function(req, res) {
});
app.get('/auth/forcedotcom/callback', passport.authenticate('forcedotcom', {
successRedirect: '/',
failureRedirect: '/login'
}), function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res) {
res.redirect('/login');
});
app.listen(3000);
console.log('localhost run in 3000');
function ensureAuthenticated(req, res, next) {
if(req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
I am trying to fix problem with already post a query in at there but that cannot resole Cannot GET /auth/twitter/callback while using twitter oauth in nodejs
looks like you've set the callback URL to http://yourdomain.com/callback in your salesforce oauth app, but have not define a handler for it correctly
either change the callback URL of your salesforce oauth app to
http://yourdomain.com/auth/forcedotcom/callback
or change the following in your code
app.get('/auth/forcedotcom/callback', passport.authenticate('...
to
app.get('/callback', passport.authenticate('forcedotcom', {
successRedirect: '/',
failureRedirect: '/login'
}), function(req, res) {
res.redirect('/');
});
I was playing around with node.js as a beginner and trying to get the login/atuthentication running but got stuck there. I then downloaded connect-flash middleware using npm and this is how my server.js looks like:
var express = require('express'),
passport = require('passport'),
util = require('util'),
LocalStrategy = require('passport-local').Strategy,
wines = require('./routes/wines');
var users = [
{ id: 1, username: 'bob', password: 'secret', email: 'bob#example.com' }
, { id: 2, username: 'joe', password: 'birthday', email: 'joe#example.com' }
];
function findById(id, fn) {
var idx = id - 1;
if (users[idx]) {
fn(null, users[idx]);
} else {
fn(new Error('User ' + id + ' does not exist'));
}
}
function findByUsername(username, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.username === username) {
return fn(null, user);
}
}
return fn(null, null);
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(
function(username, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
});
}
));
var flash = require('connect-flash');
var express = require("express");
var app = express();
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/flash', function(req, res){
req.flash('info', 'Flash is back!')
res.redirect('/');
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: "Hello!"});
});
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'keyboard cat' }));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.cookieParser('keyboard cat'));
app.use(express.session({ cookie: { maxAge: 60000 }}));
app.use(flash());
});
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000);
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
app.get('/wines', wines.findAll);
app.get('/wines/:id', wines.findById);
console.log('Listening on port 3000...');
Why do I still get that error? How to fix it?
Move all your app[METHOD] middleware to after all the app.use middleware