I'm trying to authenticate user through passport-facebook, and there is 1 really odd thing happening which im really unsure of.
When defining my routes i have this line of code
app.get('/login/facebook',
IsAuth.facebookEnter ),
//passport.authenticate('facebook')
app.get('/login/facebook/return',
IsAuth.facebookComeBack)
And in my IsAuth file i have the following
const passport = require('passport')
const FacebookStrategy = require('../passport/facebook_passport')
module.exports = {
facebookEnter(){
passport.authenticate('facebook')
},
facebookComeBack(){
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
}
}
}
and the strategy itself here
const passport = require('passport')
const User = require('../models/User')
const FacebookStrategy = require('passport-facebook')
passport.use(new FacebookStrategy({
clientID: "ALMOST POSTED IT",
clientSecret: "Ntest test",
//call baack to one of our routes to valiate the auth request
//is called when the user pressed on OK to login with facebook
callbackURL: "http://localhost:8888/login/facebook/return"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile, accessToken, refreshToken)
}
))
The question is
Why when i write
app.get('/login/facebook',
IsAuth.facebookEnter ),
it doesnt work but if i write this code
app.get('/login/facebook',
passport.authenticate('facebook') ),
then it works, so why? My JWT passport authnetication works good as expected even though using the same folder structure
How can i make it work and keep passport.authenticate in a seperate file?
Mine works with this! Dividing the exports.
module.exports.fbAuth = passport.authenticate("facebook");
module.exports.fbAuthCB = function(req, res, next) {
passport.authenticate("facebook", (err, user, info) =>
generateTokenAndRedirect(err, user, info, req, res, next)
)(req, res);
};
router.get("/auth/facebook", authCtrl.fbAuth);
router.get("/auth/facebook/callback", authCtrl.fbAuthCB);
Related
I am trying to set up some user authentication and want to use Google's OAuth2.0 to do so.
So far, I have set up a Google API Project at console.cloud.google.com and gotten an OAuth 2.0 Client ID and Client Secret (let's say clientID = 'myID.apps.googleusercontent.com' and clientSecret = 'mySecret'). Noodling around, I found that I should be using passport and I would like to use passport-google-oauth20 (Only because it has the most downloads, if there is something better PLEASE let me know). Following the instructions, I have:
.env.js
module.exports = {
google: {
clientID: 'myID.apps.googleusercontent.com',
clientSecret: 'mySecret'
}
}
app.js
const express = require('express')
const passport = require('passport')
const env = require('./.env.js')
const app = express()
const baseURL = 'https://api.mywebsite.com'
var GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: env.google.clientID,
clientSecret: env.google.clientSecret,
callbackURL: baseURL + "/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
})
}
))
app.get('/auth/google', passport.authenticate('google', {scope: ['profile']})
)
app.get('/auth/google/callback', passport.authenticate('google', {failureRedirect: '/login'}), (req, res) => {
// Successful authentication, redirect home.
res.redirect('/');
}
)
When I start the server and go to https://api.mywebsite.com/auth/google, I get redirected to https://accounts.google.com/signin/oauth/error?authError=some_mess&client_id=myID.apps.googleusercontent.com with an Error 400: redirect_uri_mismatch and referencing redirect_uri: http://127.0.0.1:5000/auth/undefined/auth/google/callback in the details. When I check the Google Console, I am explicitly allowing https://api.mywebsite.com in the Authorized JavaScript origins and all of https://api.mywebsite.com, https://api.mywebsite.com/auth/google/callback, and http://127.0.0.1:5000/auth/undefined/auth/google/callback in the Authorized redirect URIs.
How can I set this all up properly?
Bonus: If I wanted to run a validity check (say make sure the user is in a database), how could I do that?
You can set up google oauth2.0 properly in the following way:
const express = require('express')
const passport = require('passport')
const googleStrategy = require('passport-google-oauth20').Strategy;
const env = require('./.env.js')
const users =require('./../database/models/User'); //change this
const app = express()
//serialize user
passport.serializeUser((user,done)=>{
done(null,user._id);
})
//deserialize user
//on the every request deserialize function checks user whether in database
passport.deserializeUser((id,done)=>{
users.findOne({_id:new objectId(id)},(err,doc)=>{
if(err){return done(err)};
if(!doc){return done(null,false)}
return done(null,doc);
})
})
//GOOGLE STRATEGY
passport.use(new googleStrategy({
clientID: env.google.clientID,
clientSecret:env.google.clientSecret,
callbackURL:'https://api.mywebsite.com/auth/google/callback', //change this
passReqToCallback : true
},function(request,accessToken, refreshToken, profile, callback){
users.findOneAndUpdate({profile_id:profile.id},{
$setOnInsert:{
//your data that will insert when object is not found
},
$set:{last_login:new Date() //if user exists update this field
//or something you want to update
},{
upsert:true, //if object didn't found, insert new object to db
new:true //return updated data
},(err,doc)=>{
if(err){console.log(err)}
return callback(null, doc);
})
}))
};
After updating my dependencies at service I'm creating I faced the issue that my Passport JS strategy, used along with OAuth2Strategy stoped working properly.
Here are the prerequisite:
Node: 14.15.1
pnpm: 6.29.0
passport: 0.5.2
passport-oauth2 1.6.1
express-session 1.17.2
Strategy is initialised like follows:
const passport = require('passport');
const config = require('../../../../config');
const OAuth2Strategy = require('passport-oauth2');
const axios = require('axios');
const MyStrategy = new OAuth2Strategy({
// state: true,
authorizationURL: config.myapi.authorizationURL,
tokenURL: config.myapi.tokenURL,
clientID: config.myapi.clientId,
clientSecret: config.myapi.clientSecret,
callbackURL: config.myapi.callbackURL,
passReqToCallback: true,
}, () => {console.log('Fire!')}); // <- This line should be called, but it is not!
passport.use('oauth2', MyStrategy);
*Obviously, the part where fire is written should be replaced by callback function, but I replaced it for more cleaner code
And routes go this way
...
routes.get('/oauth/myapi', async (req, res, next) => {
const authParams = {
session: true,
scope: 'read, create',
state: req.csrfToken(),
};
return passport.authenticate('oauth', authParams,
async (err, passportUser, info) => {
if (err) {
return next(err);
}
if (passportUser) {
const user = passportUser;
user.token = passportUser.generateJWT();
return res.json(user.toAuthJSON());
}
res.status(400).json({error: info});
})(req, res, next);
});
routes.get('/oauth/myapi/callback',
async (req, res, next) => {
return passport.authenticate('oauth', {
// failWithError: true,
successRedirect: '/dashboard',
failureRedirect: '/login/oauth/myapi/failed'
})(req, res, next);
});
...
So in a callback I do receive response from third service and it looks like this
{
"code":"2QoCKOzHQbCdJID4m...pwHv4M1RqUKjKF",
"state":"dS9Gagcc-....a_yJ71YU",
"user_id":"101"
}
But when callback route attempts to execute passport.authenticate I receive
$ Error: Failed to obtain access token
$ at /Users/number16/Documents/GitHub/Video-Mixer-Node/node_modules/.pnpm/passport-oauth2#1.6.1/node_modules/passport-oauth2/lib/strategy.js:178:49
Debugging didn't help much either.
It seems to me that I do something wrong or I should update my code as some breaking change might require.
The problem seems to be caused by switching from 0.4.1 to 0.5.2 passport
Please, provide me with suggestions on what might cause this issue and how to resolve it.
Thanks in advance
I am attempting to use Passport.js to authorize Google OAuth2 on Node.js. I have tried all week to make it work and have no idea why it isn't, so am now resorting to stack for some potential help. I have tried all solutions to similar problems available on forums online.
Each time it sends the request it returns TokenError: Bad Request, however, it is able to console.log the required data, so this to me demonstrates that the token was in fact successful. I cannot explain why this is occurring.
I have tried being more specific in callback request e.g http://localhost:3000/auth/google/redirect.
I have tried every other type of Oauth type google has Node server, web application, html ect.
I have tried different ports.
AUTH ROUTES
const router = require('express').Router();
const passport = require('passport');
// auth login
router.get('/login', (req, res) => {
res.render('login', { user: req.user });
});
// auth logout
router.get('/logout', (req, res) => {
// handle with passport
res.send('logging out');
});
// auth with google+
router.get('/google', passport.authenticate('google', {
scope: ['profile']
}));
// callback route for google to redirect to
// hand control to passport to use code to grab profile info
router.get('/google/redirect', passport.authenticate('google'),
(req,
res) => {
res.send('you reached the redirect URI');
});
module.exports = router;
PASSPORT_SETUP
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const keys = require('./keys');
passport.use(
new GoogleStrategy({
// options for google strategy
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret,
callbackURL: '/auth/google/redirect'
}, (accessToken, refreshToken, profile, done) => {
// passport callback function
console.log('passport callback function fired:');
console.log(profile);
})
);
When submitted the process progresses through SignIn page, delivers desired result the console.log and then just sits for about 1 minute awaiting localhost.
As you can see the very thing it is trying to retrieve is already in the console.
It then progresses to throw and Error:
Sorry for the late reply, dug up some old code this is the point where it was marked as 'All auth methods functioning'. As stated by Aritra Chakraborty in the comments, "done" method was not being called. See the following implementation with Nedb.
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const Datastore = require('nedb');
const database = new Datastore('database.db');
database.loadDatabase();
passport.serializeUser((user, done) => {
done(null, user.googleId || user.id);
});
passport.deserializeUser((googleId, done) => {
database.findOne({ googleId : googleId }, (err, user) => {
done(null, user);
});
});
var strategy = new GoogleStrategy({
// options for google strategy
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret,
callbackURL: '/auth/google/redirect'
}, (accessToken, refreshToken, object0, profile, done) => {
// check if user already exists in our own db
database.findOne({ googleId: profile.id }, (err, currentUser) => {
if (currentUser !== null) {
done(null, currentUser);
} else {
var d = new Date();
var n = d.getTime();
var duoID = uuidv1();
var User = {
duoVocalID: duoID,
googleId: profile.id,
username: profile.displayName,
thumbnail: profile._json.image.url,
oscope: object0.scope,
oaccess_token: object0.access_token,
otoken_type: object0.token_type,
oid_token: object0.id_token,
oexpires_in: object0.expires_in,
oemails: profile.emails,
olanguage: profile._json.language,
oname: profile.name,
TimeOfLastLogon: n,
RefreshToken: refreshToken
};
database.insert(User, (err, newUser) => { });
var newUser = User;
done(null, newUser);
}
});
});
passport.use(strategy);
// auth with google+
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile', 'email', 'https://www.googleapis.com/auth/spreadsheets'],
accessType: 'offline',
approvalPrompt: 'force'
}));
// callback route for google to redirect to
// hand control to passport to use code to grab profile info
app.get('/auth/google/redirect', passport.authenticate('google'), async (req, res) => {
var userString = JSON.stringify(req.user)
jwt.sign({userString}, 'secretKey', { expiresIn: '365d' }, (err, token) => {
res.send("<script>localStorage.setItem('token', '"+token+"'); window.close(); window.opener.document.getElementById('modal-toggle').checked = false;</script>");
});
});
I am using a Twitter strategy with passport.js but when I hit a certain route such as auth/twitter in the app, it never redirects to the Twitter sign-in/authorization.
My passport.js
const passport = require("passport");
const TwitterStrategy = require("passport-twitter").Strategy;
passport.serializeUser(function(user, cb) {
cb(null, user.id);
});
passport.deserializeUser(function(id, cb) {
User.findOne({ id }, function(err, user) {
cb(err, user);
});
});
passport.use(
new TwitterStrategy(
{
consumerKey: "some_key",
consumerSecret: "some_secret",
callbackURL: "http://www.example.com/auth/twitter/callback"
},
function(token, tokenSecret, profile, cb) {
User.findOrCreate({ twitterId: profile.id }, function(err, user) {
return cb(err, user);
});
}
)
);
AuthController.js
const passport = require("passport");
module.exports = {
twitter: function(req, res) {
console.log("TWITTER AUTH IS RUNNING");
passport.authenticate("twitter");
console.log("NOT HANGING");
},
twitterCallback: function(req, res) {
console.log("TWITTER CALLBACK");
passport.authenticate("twitter", {
successRedirect: "/",
failureRedirect: "/search"
});
}
};
My routes.js
// AUTH
"GET /auth/twitter": "AuthController.twitter",
"GET /auth/twitter/callback": "AuthController.twitterCallback",
The console.logs do run accordingly but the app never goes to the twitter authentication portion.
UPDATE: I fixed the issue, it was most likely an issue with how I setup up passport and not including the middlewares, but now I have another issue of none of my pages being rendered. They are just blank pages and not even the favicon is being rendered in.
UPDATE: I seemed to narrow the issue down to the http.js file.
// order: [
// "session",
// "passportInit", // <==== If you're using "passport", you'll want to have its two
// "passportSession" // <==== middleware functions run after "session".
// ],
// passportInit: (function() {
// return require("passport").initialize();
// })(),
// passportSession: (function() {
// return require("passport").session();
// })()
Whenever I uncomment that block of code the pages won't render. More specifically when I only leave the order:["session"] uncommented. I think the middlewares are keeping the pages from rendering.
UPDATE: It was how left some of the middlewares out. I added all default middlewares in and now it works.
NEW ISSUE: The callback function after the initial passport.use does not fire. I tried console logging from within the callback function and nothing appears in the terminal.
I already checked multiple answers here on Stackoverflow, and also went through on the documentation but I still cannot find out what could be the problem. In my application I'm using SequelizeJS to access to my mySQL database and now I'm trying to secure my REST API endpoints with PassportJS using the JWT Strategy.
./app.js
// ...
// passport
app.use(passport.initialize());
require('./config/passport')(passport);
// ...
./config/passport.js
var passport = require('passport');
var passportJwt = require('passport-jwt');
var models = require('../models');
var config = require('./config');
var ExtractJwt = passportJwt.ExtractJwt;
var Strategy = passportJwt.Strategy;
module.exports = function(passport) {
var params = {
secretOrKey: config.jwt.secret,
jwtFromRequest: ExtractJwt.fromAuthHeader()
};
passport.use(new Strategy(params, function(jwt_payload, done) {
models.User.findOne({
where: {
id: jwt_payload.id
}
}).then(
function(user) {
if (user) {
done(null, user);
} else {
done(null, false);
}
},
function(err) {
return done(err, false);
}
);
}));
};
I'm trying to get the user entity from the request of this simple route:
var router = express.Router();
// ...
router.route('/user/me', passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});
I already created another route which returns a JWT token based on the provided username and password. When I call the /user/me endpoint I attach the JWT token into the header, for example:
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.M9z3iWNdjAu4THyCYp3Oi3GOWfRJNCYNUcXOw1Gd1Mo
So, my problem is that when I call the /user/me endpoint with a token, the req.user will be undefined and I cannot figure it out what is the reason.
Thank you in advance for your help!
Your route definition seems to be wrong: router.route doesn't accept a middleware in its second argument, so authentication does not happen at all.
It should be smth like
var router = express.Router();
// ...
router.route('/user/me')
.all(passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});