I have been struggling with implementing "Login with facebook" for two weeks and I still couldn't get it to work.
I also tried next-auth but that didn't work for multiple reasons.
I am using BlitzJS and PassportJS.
The error I am getting after adding the passport-facebook strategy is:
failed to serialize user into session
The internet is full of answers that you simply have to add two functions to passport:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
You can't to that when using BlitzJS because you don't have a passport object. I tried adding the functions inside the library itself inside node_modules (probably not the best idea anyways), now I am getting:
API resolved without sending a response for /api/auth/facebook?code=A..., this may result in stalled requests.
and the browser just loads forever.
The part of the login with facebook itself seems to work because I do get a userId and correct mail returned.
My api/auth/[...auth].ts file looks like this:
import { passportAuth } from "blitz"
var FacebookStrategy = require("passport-facebook")
import db from "db"
export default passportAuth({
successRedirectUrl: "/",
errorRedirectUrl: "/",
strategies: [
{
strategy: new FacebookStrategy(
{
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/api/auth/facebook",
},
async function (accessToken, refreshToken, profile, cb) {
if (!profile.id) return cb(new Error("Facebook auth: No profile id."))
let user = await db.user.findFirst({
where: {
facebookId: profile.id,
},
})
if (!user) {
user = await db.user.create({
data: {
facebookId: profile.id,
},
})
}
const publicData = {
userId: user.id,
role: "USER",
}
return cb(null, { publicData, redirectUrl: "/" })
})
Related
I am trying to create a React Native app in which I am using an Express server with Passport.JS to act as the login/authentication server.
My current idea for logging in is this:
Open browser tab with login page
Login with Google / Facebook
Send accessToken and refreshToken back to React Native page via deep linking
Use accessToken to authenticate API requests to server (what I am having issues with)
Google Strategy:
passport.use(
new GoogleStrategy(
google,
async (accessToken, refreshToken, profile, done) => {
console.log(accessToken, refreshToken);
User.findOne()
.lean()
.exec({ googleID: profile.id }, async (err: any, user: UserModel) => {
if (err) {
console.log(err);
done(err, undefined);
return;
}
if (!user) {
let username =
profile?.emails?.at(0)?.value.split("#")[0] || profile.id;
username = await checkUsername(username, profile);
const newUser = new User({
googleID: profile.id,
username: username,
profileImage: profile?.photos?.at(0)?.value,
dateCreated: new Date(),
});
newUser
.save()
.lean()
.exec((err: any, createdUser: UserModel) => {
if (err) {
console.log(err);
done(err, undefined);
return;
}
createdUser.accessToken = accessToken;
createdUser.refreshToken = refreshToken;
done(null, createdUser);
});
return;
}
user.accessToken = accessToken;
user.refreshToken = refreshToken;
done(null, user);
});
}
)
);
Login Routes:
app.get(
"/user/login/google",
passport.authenticate("google", {
scope: ["profile", "email"],
prompt: "select_account",
accessType: "offline",
})
);
app.get(
"/user/login/google/callback",
passport.authenticate("google", {
failureRedirect: "/auth/google",
}),
(req, res) => {
const user = req.user as UserModel;
console.log(user);
res.redirect(
`myapp://login?id=${user?._id}&accessToken=${user?.accessToken}&refreshToken=${user?.refreshToken}`
);
}
);
Can I use what I have here to send what I have as the accessToken as a Bearer and authenticate the request? Or if this is not possible what is the best way to achieve this?
From my understanding when using Passport in a React website cookies are saved and used for authorization, however as I am sending this information to React Native this method isn't working.
I have tried using passport.authenticate("google") to authenticate the request by using both the Bearer token and sending access_token in the request, but neither seem to work
I looked in several posts, but I cannot find something that meets my situation.
To login with google on my website (or signup) with google, you have to go to mydomain.com/login/google
You then, well, login, and then the callback is handled on mydomain.com/auth/google
Here is the code responsible for this.
app.get('/login/google', passport.authenticate('google'));
app.get('/auth/google',
passport.authenticate('google', { failureRedirect: '/login', failureMessage: false, session: false, failureFlash: true }),
function(req, res) {
res.redirect('/');
});
Here is where I store the users:
passport.use(new GoogleStrategy({
clientID: no,
clientSecret: definitely not,
callbackURL: 'https://no cuz privacy/auth/google'
},
async function(issuer, profile, cb) {
var user = await User.find({ google: true, googleid: profile.id })
if (!user[0]) {
// The Google account has not logged in to this app before. Create a
// new user record and link it to the Google account.
const newUser = await new User({ username: generateUsername(), google: true, googleid: profile.id, googleProfile: profile })
await newUser.save()
return cb(null, newUser);
} else {
// The Google account has previously logged in to the app. Get the
// user record linked to the Google account and log the user in.
console.log('exists')
return cb(null, newUser);
}
}
));
I think you have to do something with the callback function (cb()) to somehow go to app.get('/auth/google') for the redirect, but, all it does is print either exists or new in the console, spinning forever on the client side. Not sure how to redirect after the code determines either account exists or new account.
Thanks in advance!
P.S. I just want to point out that the cb() function could also be done() too. For example:
function(accessToken, refreshToken, profile, done){
console.log("strategy");
console.log(profile);
console.log(accessToken);
console.log(refreshToken);
done(null, profile);
}
^^ Not my code --> PassportJS in Nodejs never call the callback function ^^
Please tell me if I did anything wrong!
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'm using Node.js and passport facebook strategy to log client in app.
I followed the passport docs but still have an error: Data must be a string or a buffer.
Strategy redirects to facebook page very well, but after test accepts app's conditions and it redirects to homepage, the app throw this error:
StatusCodeError: 500 - {"error":"Data must be a string or a buffer"}
This is my code from auth.js where strategy is written. I'm using jsonwebtoken module to sign user id.
exports.facebookStrategy = new FacebookStrategy({
clientID: config.auth.facebook.clientID,
clientSecret: config.auth.facebook.clientSecret,
callbackURL: config.auth.facebook.callbackURL,
profileFields: ['id', 'displayName', 'email']
}, function (accessToken, refreshToken, profile, done) {
var userProfile = {
username: profile._json.id,
name: profile._json.name,
email: profile._json.email,
facebook: true
}
findOrCreate(userProfile, (err, user) => {
if (err) {
return done(err);
}
// use token lib to sign id
jwt.sign({ userId: user.username }, config.secret, {}, (e, token) => {
if (e) {
return done(e);
}
user.token = token;
return done(null, user);
})
});
function findOrCreate (user, callback) {
client.getUser(user.username, (err, usr) => {
if (err) {
return client.saveUser(user, callback);
}
callback(null, usr);
})
}
});
Using a console.log I figured out that error comes from this code of block:
...
findOrCreate(userProfile, (err, user) => {
if (err) {
console.log(err.message); // it returns 500 - {"error":"Data must be a string or a buffer"}
return done(err);
}
I tried to change profile._json to profile._raw. However all values are undefined.
I'm using Node 6.10.0 version. and passport: "^0.3.2", "passport-facebook": "^2.1.1".
How can I solve this error?
This error ocurr when function to create password encrypted the parameter is null. By example:
const crypto = require('crypto')
let shasum = crypto.createHash('sha256')
shasum.update(password) // password is null
This authentication method is not require password you must code a conditional to prevent the encryption.
I'm trying to implement passport.js authentication in a Sails.js app, using the Google OAuth2.0 strategy. I have considered using sails-generate-auth and sails-auth but they are no longer supported. I have also considered Waterlock but it only works with the local, Twitter and Facebook strategies.
The google() function below is called when the user presses the 'login with google+' button. The expected behaviour is that the user then gets redirected to a Google page, where they are prompted to authenticate themselves. In actuality, the following error is logged at the marked line. The user object is undefined at that point.
InternalOAuthError: Failed to obtain request token (status: 307 data: <HTML>
<HEAD>
<TITLE>Temporary Redirect</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Temporary Redirect</H1>
The document has moved here.
</BODY>
</HTML>
)
The function below is located in the AuthController and gets called upon clicking the 'login with Google+' button.
google(request, response) {
const google = sails.config.oauth.google;
passport.use(new GoogleStrategy({
consumerKey: MY_KEY,
consumerSecret: MY_SECRET,
callbackURL: 'http://localhost:1337/auth/callback/google',
}, (token, tokenSecret, profile, done) => {
//Verify callback: Never appears to get called.
User.findOrCreate({ providerId: profile.id, provider: 'Google' }).then((user) => {
request.session.me = user;
return done(user);
}).catch(error => {
sails.log('Failed to log in using Google: ' + error);
});
}
));
passport.authenticate('google', {
failureRedirect: '/login',
scope: [
'profile',
],
}, function(error, user) {
//Gets called, but user is undefined.
if (error) {
sails.log('Err: ' + error); //<== Error gets logged here.
}
request.logIn(user, function(error) {
if (error) {
sails.log('err: ' + error);
response.view('500');
return;
}
res.redirect('/');
});
})(request, response);
},
Misc information: Passport is initialized in config/policies.js:
'*': [
passport.initialize(),
passport.session(),
],
My question: What is the likely cause of the error I'm getting?
I ran into the same issue using Express, passport, and passport-google-oath. I solved this by switching topassport-google-oauth20.
I am not a 100% sure if this is why, but Google seems to have dropped OAuth support quite a while back.
Important: OAuth 1.0 was officially deprecated on April 20, 2012, and is no longer supported. We encourage you to migrate to OAuth 2.0 as soon as possible.
You'll have to change your Strategy to:
var GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));