In my application, I'm trying to add loggin with facebook account. As I'm using passport for the moment with local strategy, I tried to add the facebook strategy. I registered on facebook developper site to have my token and secret. I simply copy/paste source code portions from the official passport github, adapting to my personal use.
The problem occurs when calling
passport.authenticate('facebook');
I am stuck in here and not redirected to the facebook loggin page. My application is waiting for a reponse or waits to be redirected, but nothing happens. I tried to provide on the facebook developper page my callback URL, and I tried without it (passing the callback throw the passport strategy).
What am I missing ?
My application:
app.get('/auth/facebook', user_routes.facebookLogin);
app.get('/auth/facebook/callback', user_routes.facebookCallback);
My routes:
exports.facebookLogin = function(req, res, next) {
console.log('Try to login with facebook');
passport.authenticate('facebook'); //<---------------- does not go further than here
};
exports.facebookCallback = function(req, res, next){
passport.authenticate('facebook', {
successRedirect: '/home',
failureRedirect: '/login' });
};
My strategy:
passport.use(new FacebookStrategy({
clientID: "xxxxxxxx",
clientSecret: "xxxxxxxxxxx",
callbackURL: "http://localhost:8080/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({ username: profile.displayName, email: profile.emails[0].value }, function(err, olduser) {
if (err) { return done(err); }
if (olduser) {
return done(null, olduser);
}
else{
var newuser = new User({
username: profile.displayName,
email: profile.emails[0].value
}).save(function(err,newuser){
if(err) console.log(err);
done(null,newuser);
});
}
});
})
);
[EDIT]
Changing the routing for the callback to this solves my issue. However, I dont understand the reason why...
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/home');
});
Finally found the solution here:
Using PassportJS with Connect for NodeJS to authenticate Facebook Users
and here:
Facebook Authentication with Passport and ExpressJS - Why is verify callback not called?
Related
I saw all the possible answers online but I just cannot get this to work and don't understand why. First problem I came across is the usual: "URL Blocked: This redirect failed because the redirect URI is not whitelisted in the app's Client OAuth Settings". After a few hours trial and error, trying every possible way (http, https, www, and without) and I managed the app to save the FacebookId in mongoDB (although didn`t prompt me to the Facebook login page), but right after now I have Cannot GET /auth/facebook message.. Now I know I have an issue already in mondoDB because at this point no multiple account can be saved without and email address as username would be NULL and only one allowed, but after wiping the DB I can sign in with Google oath without and issue so, it seems like I have a problem setting up
Facebook and i just don`t know what im doing wrong. Thank you in advanced!
facebooksettings
facebooksettings2
the site url set to: https://app-secret.herokuapp.com/
app.js
...
//use session package with some setup config//
app.use(session({
secret: 'My little secret.',
resave: false,
saveUninitialized: false,
}))
//initalize passport packadge and for also to deal with the session//
app.use(passport.initialize());
app.use(passport.session());
mongoose.connect("mongodb+srv://andras:MYPASSWORD#cluster0.zfr0d.mongodb.net/userDB", {
useNewUrlParser: true,
useUnifiedTopology: true
});
//schema in order to have a plugin it has to be a mongoose schema//
const userSchema = new mongoose.Schema({
email: String,
password: String,
googleId: String,
facebookId: String,
secret: Array
});
//adding plugins to schema//
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);
const User = new mongoose.model("User", userSchema);
//configuring passport, serialize=create and deserialize=able to crack open cookie//
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "https://app-secret.herokuapp.com/auth/google/secrets",
userProfileURL: "https://www.googleapis.com/oauth2/v3/userinfo"
},
function(accessToken, refreshToken, profile, cb) {
// console.log(profile);
User.findOrCreate({
googleId: profile.id
}, function(err, user) {
return cb(err, user);
});
}
));
passport.use(new FacebookStrategy({
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_APP_SECRET,
callbackURL: "https://app-secret.herokuapp.com/auth/facebook"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({
facebookId: profile.id
}, function(err, user) {
return cb(err, user);
});
}
));
app.get("/", function(req, res) {
res.render("home")
});
app.get("/auth/google",
passport.authenticate('google', {
scope: ["profile"]
})
);
app.get("/auth/google/secrets",
passport.authenticate('google', {
failureRedirect: "/login"
}),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/secrets');
});
app.get('/auth/facebook',
passport.authenticate('facebook'));
app.get('/auth/facebook/secrets',
passport.authenticate('facebook', {
failureRedirect: '/login'
}),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/secrets');
});
So sorry to spam stackoverflow with my rookie misery. I realised where i went wrong, i thought the facebook API is the one creating me headache and not realising that the facebook callback in the app.js missing the correct path.. instead of: "app-secret.herokuapp.com/auth/facebook" it should be: "app-secret.herokuapp.com/auth/facebook/secrets"
I'm in the process of learning how to use passport and evaluating it for my personal projects. I wrote the smallest possible amount of code that I expected to get passport going:
import passport from "passport"
import passportLocal from "passport-local"
//...
const app = express()
//...
app.use(passport.initialize())
app.use(passport.session())
type User = {
id: string
username: string
}
passport.serializeUser((user: User, done) => {
console.log("serializeUser", user)
done(null, user.id)
})
passport.deserializeUser(function (id, done) {
console.log("deserialize", id)
done(null, {id: 1, username: "sam"})
})
passport.use(new passportLocal.Strategy((username, password, done) => {
console.log("local", username, password)
return done(null, {id: 1, username: "sam"})
}))
app.post("/login", passport.authenticate("local"))
This is an API, so, it should just read and respond JSON. The frontend makes a post request to /login with username and password:
and the result is a 404. On the backend, I can see:
local sam monkey123
serializeUser { id: 1, username: 'sam' }
POST /login 404 22.516 ms - 145
What is causing this to be a 404?
After successful login, passport doesn't know what to do (or does something by default what you don't have), so you get a 404. You need to specify that as the third param of the login route (as it is in the docs http://www.passportjs.org/packages/passport-local/):
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
}
);
Or alternatively if you want to have an API:
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) {
res.json({
success: true, // or something to indicate to the frontend that you've identified the user
user: req.user // or something (a token maybe what you'll use later)
});
}
);
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;
all! I've Node.js, Express and Swagger(use swagger-express-mw and swagger-tools/middleware/swagger-ui).
I know how to implement OAuth Facebook login to Node.js and Express but swagger stops logic.
For example, this logic works:
app.get('/auth/facebook',
passport.authenticate('facebook'));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
passport.use(new FacebookStrategy({
clientID: process.env.FB_ID,
clientSecret: process.env.FB_SECRET,
callbackURL: 'http://localhost:5100/api/auth/facebook/callback'
},
function(accessToken, refreshToken, profile, cb) {
console.log('accessToken, refreshToken, profile, cb');
console.log(accessToken, refreshToken, profile, cb);
// User.findOrCreate({ facebookId: profile.id }, function (err, user) {
// return cb(err, user);
// });
// return profile
}
));
But If I implement using Swagger I don't get any actions after "/login/facebook":
/auth/facebook/callback:
x-swagger-router-controller: Authorization
get:
operationId: facebookCallback
summary: Facebook login
tags:
- facebook
responses:
"200":
description: Successful response
schema:
$ref: "#/definitions/Response"
/login/facebook:
x-swagger-router-controller: Authorization
get:
operationId: facebook
summary: Facebook login
tags:
- facebook
responses:
"200":
description: Successful response
schema:
$ref: "#/definitions/Response"
function facebook(req, res, next) {
console.log('Stopped here');
passport.authenticate('facebook');
}
function facebookCallback(req, res, next) {
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
console.log('Callback wasn\'t called');
// Successful authentication, redirect home.
res.redirect('/');
}
}
So, I don't understand why cann't see facebook login page?
I'm trying to develop a simple API in node and hiding it behind a google authentification layer, for that I'm using passport-google-oauth2 to enforce a google authentification to do some requests.
....
var GoogleStrategy = require('passport-google-oauth2').Strategy;
var passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(new GoogleStrategy({
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
callbackURL: "/oauth2callback",
passReqToCallback : true
},
function(request, accessToken, refreshToken, profile, done) {
console.log("passportUse access Profile ", accessToken);
done(null, accessToken);
}
));
app.get('/oauth2callback',
passport.authenticate( 'google', {
successRedirect: '/notesSuccess',
failureRedirect: '/notesNotSuccess'
}));
app.get('/notes',
passport.authenticate('google', { scope:
[ 'https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/drive'] }
), function(req, res) {
//res.json({notes: "This is your notebook. Edit this to start saving your notes!"})
})
app.get('/notesSuccess', function(req, res) {
res.json({notes: "This is your notebook. Edit this to start saving your notes!"})
})
app.get('/notesNotSuccess', function(req, res) {
res.json({notes: "AUTHENTICATION FAIL"})
})
app.get('/notes/API1', function(req, res) {
res.json({notes: "API1111111"})
})
....
The code is working more or less, at passport.use I do get all the google plus profile information and a token, so I assume that part is working.
The issue is that when I use the browser to visit localhost:8080/notes, I get the results as expected (/notesSuccess) when I login, if I'm in a private window I can see the google login page, working well so far.
My problem is if I do a query with postman using the token I was given or a new token with the same scopes I get only the login page (as if I had no Authentification header). If I query notes/API1 (no Oauth there) it works properly
Any ideas why the headers are not authentifing the query?