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.
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 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: "/" })
})
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 have a express nodejs backend which has three URL functions in which
1) registerUser() added user details to database and provided a JWT for the caller
2) verifyToken()- verifies if the JWT is valid
3) getConfiguration()- if JWT is verified from above function provides user with some configuration data
So the express code I'm using to achieve this is
//Routes.js
app.use(requestIp.mw())
app.route('/register')
.post(userController.registerUser);
app.use(userController.verifyToken)
app.route('/user/configuration')
.post(chayakkadaController.getConfiguration);
Now my issue is whenever I try calling the URL /register instead of calling registerUser function it calls verifyToken and says my token is invalid ( I want registerUser function to work without token, but getConfiguration should work only with token)
This is my verifyToken function
export function verifyToken(req, res, next) {
var token = req.body.token || req.headers["token"];
var appData = {};
if (token) {
jwt.verify(token, process.env.SECRET_KEY, function (err, decoded) {
if (err) {
appData["status"] = 1;
appData["error"] = "Invalid Token";
res.status(500).json(appData);
} else {
req.user = decoded;
next();
}
});
} else {
appData["status"] = 1;
appData["error"] = "Need access token";
res.status(403).json(appData);
}
}
My register User code
export function registerUser(req, res) {
let userData = {
device: req.body.device,
device_version: req.body.device_version,
device_id: req.body.device_id,
app_version: req.body.app_version,
app_id: 2,
ip_address: req.headers['x-real-ip'] || req.connection.remoteAddress
}
database.query(`INSERT INTO users SET ?`, userData)
.then(result => {
let user = {
id: result.insertId
}
let token = jwt.sign(user, process.env.SECRET_KEY);
let appData = {};
appData["token"] = token;
redis.sendMessage({
qname: 'registration_queue',
message: result.insertId + '',
}, (err, resp) => {
res.status(201).json(appData);
});
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
}
Why you wanna to invent the wheel? there is a NPM module for that:
express-jwt
It has middleware that checks the jwt, if it valid, it decodes the payload and adds it to the request after that it proceed to your controller, if it is not valid, it throws an error, that you should catch, and do what ever you want.
It has the unless feature, so you can configure the entire subpath as restricted unless it is /register
router.use(`admin/`, [
expressJwt({ secret: jwtSecret }).unless({
path: ['/register]
}),
]);
I am using passport GoogleStrategy to authenticate users, but i am getting below error. Can you please help me ?
Code
passport.use(new GoogleOAuth2Strategy({
clientID : configAuth.googleAuth.clientID,
clientSecret: configAuth.googleAuth.clientSecret,
callbackURL: configAuth.googleAuth.callbackURL,
scope: ['profile', 'email', 'openid'],
passReqToCallback : true
},
function(token, refreshToken, profile, done) {
// User.findOne won't fire until we have all our data back from Google
process.nextTick(function() {
console.log(profile.id);
User.findOne({ 'google.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
return done(null, user);
} else {
var newUser = new User();
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.email = profile.emails[0].value;
console.log(profile.id);
console.log(token);
console.log(profile.displayName);
console.log(profile.emails[0].value);
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
Error Log
500 failed to obtain access token (Error: connect ECONNREFUSED) at
D:\Chandrasekhar\NodeJs_Workspace\SkillsetMgmtNode\node_modules\passport-google-oauth\node_modules\passport-oauth\lib\passport-oauth\strategies\oauth2.js:125:38 at
D:\Chandrasekhar\NodeJs_Workspace\SkillsetMgmtNode\node_modules\passport-google-oauth\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:177:18
at ClientRequest.
(D:\Chandrasekhar\NodeJs_Workspace\SkillsetMgmtNode\node_modules
\passport-google-oauth\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:148:5)
I too was getting the same error . For me the error was because i was behind a proxy . When run without proxy the code worked fine . So try this once .