i am new to typescript and i am working on an Oauth2.0 project with passport, the req.user returns undefined and i dont know why, here is my code.
function checkLoggedIn(req: Request, res: Response, next: NextFunction): any {
console.log('current user is: ', req.user);
const isLoggedIn = true;
if (!isLoggedIn) {
return res.status(401).json({
error: 'you must log in',
});
}
next();
}
i always get undefined when logging the user
current user is: undefined
anyone knows what is the course of this?
the whole code
async function verifyCallback(accessToken: any, refreshToken: any, profile: any, done: any) {
const newUser = {
googleId: profile.id,
displayName: profile.displayName,
email: profile.emails[0].value,
Image: profile.photos[0].value,
};
try {
let user = await Oauth.findOne({ googleId: profile.id });
if (user) {
done(null, profile);
} else {
user = await Oauth.create(newUser);
done(null, profile);
}
} catch (err) {
console.log(err);
}
}
passport.use(new Strategy(AUTH_OPTIONS, verifyCallback));
// save the session to the cookie
passport.serializeUser((user: any, done) => {
done(null, user.id)
});
// load the session from the cookie
passport.deserializeUser((id: any, done) => {
done(null, id)
});
app.use(helmet());
app.use(cookieSession({
name: 'session',
maxAge: 24 * 60 * 60 * 1000,
keys: [ config.COOKIE_KEY_1, config.COOKIE_KEY_2 ],
}))
app.use(passport.initialize());
app.use(passport.session());
function checkLoggedIn(req: Request, res: Response, next: NextFunction): any {
console.log('current user is: ', req.user);
const isLoggedIn = true;
if (!isLoggedIn) {
return res.status(401).json({
error: 'you must log in',
});
}
next();
}
// Authentication route
router.get(
'/auth/google',
passport.authenticate('google', {
scope: ['profile', 'email', 'https://www.googleapis.com/auth/youtube.upload'],
}),
);
router.get(
'/auth/google/callback',
passport.authenticate('google', {
failureRedirect: '/failure',
successRedirect: '/user',
session: true,
}),
(req, res) => {
console.log('google called us back');
},
);
router.get('/user', checkLoggedIn, async (req, res) => {
try {
const user: any = await Oauth.findOne({ username: String });
res.status(200).json(user);
} catch (err) {
res.status(500).json(err);
}
});
router.get('/logout', (req, res) => {
res.redirect('/')
});
router.get('/failure', (req, res) => {
res.send('failed to log in');
});
**
Try this
**
You can change the value (req.user) to (req.body.user)
function checkLoggedIn(req: Request, res: Response, next: NextFunction): any {
console.log('current user is: ', req.body.user);
const isLoggedIn = true;
if (!isLoggedIn) {
return res.status(401).json({
error: 'you must log in',
});
}
next();
}
Related
I'm developping an app where I want the user to login with thier discord account (Need do some check; checking user guilds, and roles) then when the user met the requirements, then I can allow that user authenticate with twitter to get some permissions. The first created session(session.sid) is overided by the last strategy used.
I'm using Mongodb to store users sessions, twitter, and discord information. I want to keep the previous sessions for other provider.
Routes part:
router.get('/discord', passport.authenticate('discord'), (req, res) => {
res.send(200)
})
router.get('/discord/redirect', passport.authenticate('discord'), (req, res) => {
res.send({msg: "Success loging with discord..."})
})
router.get('/discord-status', (req, res) => {
return req.user ? res.send(req.user):res.send({error: 403, message: 'Unauthorized user.'
})
})
router.get('/twitter', passport.authenticate('twitter', { failureRedirect: '/login', failureMessage: true }),
function(req, res) {
res.redirect('/');
})
router.get('/twitter/redirect', passport.authenticate('twitter'), (req, res) => {
res.send({msg: "Success loging with twitter..."})
})
router.get('/twitter-status', (req, res) => {
console.log(req.user)
return req.user ? res.send(req.user):res.send({error: 403, message: 'Unauthorized user.'
})
})
Discord passport part:
import passport from 'passport';
import { Profile, Strategy } from 'passport-discord';
import { VerifyCallback } from 'passport-oauth2';
import { User } from '../database/schemas';
import { config } from "dotenv"
// get env variables
config()
passport.serializeUser((user: any, done) => {
console.log(`Serialization: ${user} - ${user.id}`)
return done(null, user.id);
});
passport.deserializeUser(async (id: string, done) => {
console.log(`Deserialization: ${id}`)
try {
const user = await User.findById(id);
return user ? done(null, user) : done(null, null);
} catch (err) {
console.log(err);
return done(err, null);
}
});
passport.use(
new Strategy(
{
clientID: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
callbackURL: process.env.DISCORD_CALLBACK_URL,
scope: ['identify', 'email', 'guilds'],
},
async (
accessToken: string,
refreshToken: string,
profile: Profile,
done: VerifyCallback
) => {
const { id: discordId } = profile;
try {
const existingUser = await User.findOneAndUpdate(
{ discordId },
{ accessToken, refreshToken },
{ new: true }
);
if (existingUser) return done(null, existingUser);
const newUser = new User({ discordId, accessToken, refreshToken });
const savedUser = await newUser.save();
return done(null, savedUser)
// TO NOT AUTHORIZE THE USER WHEN ISN'T IN GUILD
// AND HASN'T THE REQUIRED ROLE
// return done(null, false)
} catch (err) {
console.log(err);
return done(err as any, undefined);
}
}
)
);
Twitter passport part:
const passport = require('passport')
import {config} from "dotenv"
var Strategy = require('passport-twitter');
import { TwitterAccounts } from '../database/schemas';
// get env variables
config()
passport.serializeUser((user: any, done: any) => {
console.log(`Serialization: ${user} - ${user.id}`)
return done(null, user.id);
});
passport.deserializeUser(async (id: string, done: any) => {
console.log(`Deserialization: ${id}`)
try {
const user = await TwitterAccounts.findById(id);
return user ? done(null, user) : done(null, null);
} catch (err) {
console.log(err);
return done(err, null);
}
});
passport.use(
new Strategy(
{
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET_KEY!,
callbackURL: process.env.TWITTER_CALLBACK_URL,
},
async (
token: any,
tokenSecret: any,
profile: any,
cb: any
) => {
const { id: twitterId, username: userName } = profile;
try {
const existingUser = await TwitterAccounts.findOneAndUpdate(
{ twitterId },
{ token, tokenSecret, userName},
{ new: true }
);
console.log({ twitterId }, { token, tokenSecret, userName})
if (existingUser) return cb(null, existingUser);
const newUser = new TwitterAccounts({ twitterId, token, tokenSecret, userName });
const savedUser = await newUser.save();
return cb(null, savedUser)
} catch (err) {
console.log(err);
return cb(err as any, undefined);
}
}
)
);
The discord part is working and the session persist:
Session {
cookie: {
path: '/',
_expires: 2023-10-25T15:47:55.818Z,
originalMaxAge: 31104000000,
httpOnly: true
},
passport: { user: '635e9cab54916ceacbb2035c' }
}
but for the twitter it isn't
Session {
cookie: {
path: '/',
_expires: 2023-10-25T15:38:35.503Z,
originalMaxAge: 31104000000,
httpOnly: true
},
passport: {}
}
As you see the passport key is empty.
How to handel multiple cookie please, any help or advice will be appreciated. Thanks
Here is my code to authenticate user based on google email :
router.use(passport.initialize());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new GoogleStrategy({
clientID: config.googleClientID,
clientSecret: config.clientSecret,
callbackURL: host + "/google-login/callback"
}, (accessToken, refreshToken, profile, done) => {
const user = {}
user.name = profile.displayName;
user.email = profile.emails[0].value;
user.photo = profile.photos[0].value;
return done(null, user);
}
));
router.get('/', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/callback', passport.authenticate('google', { failureRedirect: '/signup' }),
async (req, res) => {
await register(req, res);
res.redirect('/google-login/popup');
});
router.get('/popup', (req, res, next) => {
res.render('login/popup-close');
});
router.get('/popup-done', authorize(), async(req, res, next) => {
const account = await getAccount({ id: req.user.id, include: ['firstImpression', 'role'] });
// if(req.user.role === Role.Admin) return res.redirect(`/accounts/admin5852`);
const { section, data } = req.query;
if(section == 'checkout') {
redirect();
} else {
if(account.firstImpression) {
account.firstImpression = false;
await account.save();
res.redirect(`/accounts/dashboard#section=guide`);
} else {
redirect();
}
}
function redirect() {
if(section == 'undefined') {
res.redirect(`/accounts/dashboard`);
} else {
res.redirect(data ? `/accounts/dashboard#section=${section}&data=${data}` : `/accounts/dashboard#section=${section}`);
}
}
});
I get this error sometimes Not all the times but sometimes and this makes it really hard to understand the bug:
{"name":"InternalOAuthError","message":"Failed to fetch user profile","oauthError":{"errno":-101,"code":"ENETUNREACH","syscall":"connect","address":"2a00:1450:4017:808::200a","port":443}}
How can I debug this? How to fix this?
When I make a fetch request from my react frontend to login using passport.authenticate('./local), my passport.serializeUser is called but passport.deserializeUser is NOT (and req.user is not set).
I've read as many answers to this question on stackoverflow but to no avail. Here is my code below.
All this comes before my routes on the server.js:
//Express body parser
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.json());
//Express Session
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {
secure: false
}
}));
// Passport config
require('./config/passport')(passport);
//Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
Here is the passport config file:
module.exports = function(passport) {
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, async function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
const match = await bcrypt.compare(password, user.password);
if (!match) { return done(null, false); }
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
console.log('this gets called logged)
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('this does NOT GET LOGGED');
User.findById(id, function(err, user) {
done(err, user);
});
});
};
Here is the react fetch request for the login route:
fetch('http://localhost:5000/authentication/login', {
method: 'POST',
body: JSON.stringify(this.state.formData),
headers: {"Content-Type": "application/json"},
mode: 'cors'
})
.then(res => res.json())
.then(resObject => {
if (resObject.errors) {
console.log('errors')
} else {
this.props.dispatch(handleLoginuser(resObject.user))
this.setState({
redirect: true
})
}
});
Here is the fetch request for another random route that should be protected:
componentDidMount() {
fetch('http://localhost:5000/protectedroute', {
method: 'GET',
mode: 'cors',
credentials: 'include'
})
.then(res => res.json())
.then(resObject => {
if(resObject.loggedIn) {
this.setState({
loggedIn: true
})
}
});
}
Here are the login and protected routes:
app.post('/authentication/login', passport.authenticate('local'), (req, res) => {
res.json({errors: false, user: req.user})
});
app.route('/protected')
.get(function(req, res) {
//req.user will be undefined!
if (req.user) {
return res.json({loggedIn: true})
} else {
return res.json({loggedIn: false})
}
})
I believe it's because of your post/login route on the back end.
Calling the passport.authenticate middleware does not complete the login process unless you use the boilerplate code from the official docs (which does not work with React)
You need to call req.login to complete the login process. Check out this example
app.post('/authentication/login', (req, res, next) => {
passport.authenticate('local', (err, theUser, failureDetails) => {
if (err) {
res.status(500).json({ message: 'Something went wrong authenticating user' });
return;
}
if (!theUser) {
res.status(401).json(failureDetails);
return;
}
// save user in session
req.login(theUser, (err) => {
if (err) {
res.status(500).json({ message: 'Session save went bad.' });
return;
}
console.log('---123456789098765432345678---', req.user);
res.status(200).json({{errors: false, user: theUser}});
});
})(req, res, next);
});
Setting up withCredentials: true while sending the post request worked for me.
axios.post(uri, {
email: email,
password: password
}, {
withCredentials: true
})
I am persistently getting 'unauthorized' error while authenticating using a JWT. Below is my controller code:
exports.loginPost = async (req, res) => {
winston.info('Calling loginPost()...');
passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
return utils.errorHandler(res, err);
} else if (!user) {
return utils.errorHandler(res, {
statusCode: 403,
message: 'Incorrect username or password.'
});
}
const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
//req.user = user;
return res.json({ user, token });
// req.login(user, { session: false }, (err) => {
// if (err) {
// res.send(err);
// }
// // generate a signed json web token with the contents of user object and return it in the response
// const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
// //req.user = user;
// return res.json({ user, token });
// });
})(req, res);
};
exports.isUserLoggedIn = async (req, res) => {
let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(login);
//return res.status(200).json(req.user);
};
and passport.js strategy script is as follows:
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, async function (username, password, cb) {
//this one is typically a DB call. Assume that the returned user object is pre-formatted and ready for storing in JWT
try {
let user = await userService.getUserWithPassword(username, password);
console.log("passport.js: ",user);
if (!user || user.walletKey !== password) {
throw { statusCode: 403, message: 'Incorrect username or password.' };
}
// // purge password field
// delete user.currentPassword;
return cb(null, user, { message: 'Logged In Successfully' });
} catch (err) {
cb(err);
}
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: sharedSecret,
passReqToCallback: true
},
async function (req, jwtPayload, cb) {
// Return user object from the JWTPayload
try {
let user = await userService.getUserWithPassword(jwtPayload.walletName, jwtPayload.walletKey);
console.log("passport.js: ",user);
req.user = user
return cb(null, user); //jwtPayload
} catch(err){
return cb(err,false);
}
}
));
I am able to generate token successfully, however, on calling isUserLoggedIn method using Bearer Token, it's prompting me unauthorized error. I am not making an traditional db call to login, instead I am just creating account in a Hyperledger-Indy pool nodes. Using swagger express middleware on a Node.js app.
Adding isUserLoggedIn method script below:
exports.isUserLoggedIn = async (req, res) => {
//let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(req.user);
//return res.status(200).json(req.user);
};
I have code in router
router.post('/auth', function(req, res) {
oauth.auth(req, res);
});
correctly hitting
accesstokenController.auth = function(req, res) {
console.log('Here auth called');
passport.initialize(), passport.authenticate(
'local', {
session: false,
scope: []
},(req,res)), serialize, generateToken, respond
};
(req,res) added after getting a link which suggest this
I belive it should now call
passport.use(new Strategy(
function(username, password, done) {
console.log('Here pass called with ' + username + ' - ' + password);
db.authenticate(username, password, done);
}
));
But it never call and timeout occured.
If Id drectly call like this
app.post('/auth', passport.initialize(), passport.authenticate('local', { session: false,scope: [] }), serialize, generateToken, respond);
this is OK,
In my above method
accesstokenController.auth = function(req, res) {
console.log('Here auth called');
passport.initialize(), passport.authenticate(
'local', {
session: false,
scope: []
},(req,res)), serialize, generateToken, respond
};
I have just created a separate method and called it from router page, rather than calling this itslef
What I am missing
Other code
const db = {
updateOrCreate: function(user, cb) {
cb(null, user);
},
authenticate: function(username, password, cb) {
console.log('Here called');
User.findOne({ username: username,
password: password }).exec(function (err, user) {
if (err) {
cb(null,null);
}
else {
cb(null,user);
}
});
}
}
function serialize(req, res, next) {
console.log('Here pass called with ser ');
db.updateOrCreate(req.user, function(err, user) {
if (err) {
return next(err);
}
// we store information needed in token in req.user again
req.user = {
id: user.id
};
next();
});
}
function generateToken(req, res, next) {
req.token = jwt.sign({
id: req.user.id,
}, SECRET, {
expiresIn: TOKENTIME
});
next();
}
function respond(req, res) {
res.status(200).json({
user: req.user,
token: req.token
});
}
I have many link related to that but did not manage to solve this
Your strategy needs to return done() without which the Strategy doesn't know when it's completed, thus resulting in a timeout.
Difficult to say if this is the exact problem without further context.