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
Related
I am trying to implement a passport oAuth login and signup API that will save a user to MongoDB on signup and login to an existing user.. I used my google mail accounts(3 of them) for testing but for some reason, it only signup and login to only one of my mail and then sends an error on an attempt to signup the other accounts. below is the passport code and controller code
controller code
async function ssoLogin(req, res, next){
// declare token;
let token;
// declare rider with sso login
let riderSSO;
try{
const { _raw, _json, ...userProfile } = req.user;
console.log(userProfile)
//validate sso details that will be added into model
const { error } = ssovalidateRider({
email: userProfile.emails[0].value.toLowerCase().trim(),
firstName: userProfile.name.familyName,
lastName: userProfile.name.givenName,
googleId: userProfile.id
});
if (error)
return res.status(400).json({
error: error.details[0].message,
});
// check if a user is already existing...
riderSSO= await RiderModel.findOne({googleId: userProfile.id});
if(riderSSO){
// create a Token
token= riderSSO.generateAuthToken(riderSSO._id, riderSSO.email)
delete riderSSO['__v'];
}else{
// Register new user here
riderSSO = await RiderModel.create({
// email: _json.email.toLowerCase().trim(),
email: userProfile.emails[0].value.toLowerCase().trim(),
googleId: userProfile.id,
firstName: userProfile.name.familyName,
lastName: userProfile.name.givenName
});
// create a Token
token= riderSSO.generateAuthToken(riderSSO._id, riderSSO.email)
delete riderSSO['__v'];
}
res.status(201).json({
status: 'success',
data: req.user,
token,
data: riderSSO
});
}catch(err){
console.log(err)
res.status(400).json({
sucess:'fail',
message: `unable to login or signup ${err}`,
});
}
}
passport config code:
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_1_ID,
clientSecret: process.env.CLIENT_1_SECRET,
callbackURL: "http://localhost:4000/api/v1/rider/google/callback",
proxy: true
}, (accessToken, refreshToken, profile, done) => {
console.log(accessToken);
return done(null, profile)
}));
error:
TypeError: Cannot read property '1' of null
at model.mongodbErrorHandler (C:\Users\peter\Desktop\driveme-backend-master\node_modules\mongoose-mongodb-errors\lib\plugin.js:19:49)
at callMiddlewareFunction (C:\Users\peter\Desktop\driveme-backend-master\node_modules\kareem\index.js:482:23)
at next (C:\Users\peter\Desktop\driveme-backend-master\node_modules\kareem\index.js:163:9)
at Kareem.execPost (C:\Users\peter\Desktop\driveme-backend-master\node_modules\kareem\index.js:217:3)
at _handleWrapError (C:\Users\peter\Desktop\driveme-backend-master\node_modules\kareem\index.js:245:21)
at _cb (C:\Users\peter\Desktop\driveme-backend-master\node_modules\kareem\index.js:304:16)
at C:\Users\peter\Desktop\driveme-backend-master\node_modules\mongoose\lib\model.js:341:9
at C:\Users\peter\Desktop\driveme-backend-master\node_modules\kareem\index.js:135:16
at processTicksAndRejections (internal/process/task_queues.js:79:11)
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 having a big issue that I'm not sure is just a code design issue or I'm trying to do something that makes no sense.
I've implemented a local sign up using passport for authentication and JWT for route access and permissions. I send the token to the front end and save it to LocalStorage using AngularJS.
Everything there works and makes sense. But with twitter, I can't figure out how to implement the same strategy for getting the user logged in.
I've set up everything on the Twitter side, use passport for login and registering. But I don't see how it is possible to send the token to the front end because I can only use a GET request to receive the information from the Twitter API.
I redirect to Twitter login, redirect to the callback URL. But what comes after that? I have some relevant code that just returns the JWT. What I'm asking is how can I pass that to Angular in the best way possible?
Routes:
app.get('/login/twitter', passport.authenticate('twitter'));
app.get('/login/twitter/callback', function(req, res) {
passport.authenticate('twitter' , function(err, user, info) {
if(err) {
res.json({
'message': err
});
}
var token;
token = user.generateJwt();
res.status(200);
res.json({
"token" : token
});
})(req, res);
});
Passport:
passport.use(new TwitterStrategy({consumerKey: auth.twitterAuth.consumerKey, consumerSecret: auth.twitterAuth.consumerSecret, callbackURL: auth.twitterAuth.callbackURL}, function(req, key, keySecret, profile, done) {
User.findOne({'twitter.id' : profile.id}, function(err, user) {
if(err) {
return done(err);
}
if(user) {
if(!user.twitter.token) {
user.twitter.token = key;
user.twitter.username = profile.username;
user.twitter.displayName = profile.displayName;
user.save(function(err) {
if(err) {
res.json({
'message': err
});
}
return done(null, user);
});
}
return done(null, user);
}
let newUser = new User();
newUser.twitter.id = profile.id;
newUser.twitter.token = key;
newUser.twitter.username = profile.username;
newUser.twitter.displayName = profile.displayName;
newUser.twitter.registerDate = Date.now();
newUser.save(function(err) {
if(err) {
res.json({
'message': err
});
}
return done(null, newUser);
});
});
}));
The best way to do this from my experience is to pass the information you want to store in the JWT to the url and store the token in localStorage on the callback page. Once the user identifier is stored in the browser, redirect to the desired page.
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 attempting to authenticate a user using Box.com OAuth2.0. I make the initial call and login which redirects to my callback url with the authorization code. At this point my server handles the callback using passport but for some reason it returns a 302 and redirects to the beginning of the oauth authentication process.
//box authentication routes
app.get('/api/box', passport.authorize('box'));
// the callback after box has authorized the user
app.get('/api/box/callback', passport.authorize('box', {
successRedirect: '/',
failureRedirect: '/login'
})
);
I verified that my route is being called by using my own handler and the request data seems to be correct. Box returns a 200 and the url contains the authorization code.
app.get('/api/box/callback', function(req, res) {
console.log('auth called')
});
This is my passport strategy:
passport.use(new BoxStrategy({
clientID: config.box.clientID,
clientSecret: config.box.clientSecret,
callbackURL: config.box.callbackURL,
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
process.nextTick(function() {
if(!req.user) {
// try to find the user based on their google id
User.findOne({ 'box.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
// if a user is found, log them in
return done(null, user);
} else {
// if the user isnt in our database, create a new user
var newUser = new User();
// set all of the relevant information
newUser.box.id = profile.id;
newUser.box.accessToken = accessToken;
newUser.box.refreshToken = refreshToken;
newUser.box.name = profile.name;
newUser.box.email = profile.login;
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
} else {
// user already exists and is logged in, we have to link accounts
var user = req.user;
// update the current users box credentials
user.box.id = profile.id;
user.box.accessToken = accessToken;
user.box.refreshToken = refreshToken;
user.box.name = profile.name;
user.box.email = profile.login;
// save the user
user.save(function(err) {
if (err)
throw err;
return done(null, user);
});
}
});
}
));
Would appreciate any insight as to what might be causing this redirect behavior.
It ended up being an instance of PeerServer that was somehow causing the redirect.