Passport authenticate callback not being called - node.js

I had a problem with Passport's authenticate method. The callback supplied to the method was not called.
The code was as follows:
import { Router, Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import passport from 'passport';
// ... some stuff
router.post('/login',
(req: Request, res: Response, next: NextFunction) => {
passport.authenticate('local', { session: false }, (err, user, info) => {
// not reaching this part
if (err) {
return next(err);
}
if (err || !user) {
return res.status(400).json({
message: info ? info.message : 'Login failed',
user,
});
}
req.login(user, { session: false }, loginErr => {
if (loginErr) {
res.send(loginErr);
}
const token = jwt.sign(user, secrets.secret);
return res.json({ user, token });
});
return res.status(500).send('Shouldn\'t come here');
});
},
(err: any, req: Request, res: Response, next: NextFunction) => {
return res.status(err.status || 500).send(err.message)
});

I missed a crucial part of the example I based my code on. I had to use passport.authenticate as middleware. So, I used passport.authenticate to generate a function and then passed req, res and next to it like so:
router.post('/login',
(req: Request, res: Response, next: NextFunction) => {
// vvv THIS VARIABLE
const middleware = passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
return next(err);
}
if (err || !user) {
return res.status(400).json({
message: info ? info.message : 'Login failed',
user,
});
}
req.login(user, { session: false }, loginErr => {
if (loginErr) {
res.send(loginErr);
}
const token = jwt.sign(user, secrets.secret);
return res.json({ user, token });
});
return res.status(500).send('Shouldn\'t come here');
});
// vvv AND THIS FUNCTION CALL
middleware(req, res, next);
},
(err: any, req: Request, res: Response, next: NextFunction) => {
return res.status(err.status || 500).send(err.message)
});
And everything worked.

Related

nodejs middleware typescript generic question

interface CustomRequest<T> extends Request {
user?: IUser,
body: T
}
interface CreateBody {
email:string;
}
interface deleteBody {
id:string;
}
router.post('/create', authenticate, (req:CustomRequest<CreateBody>, res) => {
Content.Create({
email:req.body.email
})
}
router.post('/delete', authenticate, (req:CustomRequest<deleteBody>, res) => {
Content.Create({
id:req.body.id
})
}
export const authenticate = async (
req: CustomRequest<null>
res: Response,
next: NextFunction,
) => {
try {
const user = await User.findToken(req.cookies.auth);
if (!user) return res.send({ success: false });
req.user = user;
next();
} catch (err) {
return res.send({ success: false });
}
};
I want to specify the request type by situation when designating middleware in nodejs.
req:CustomRequest
I've tried generics this way, but it doesn't work.
How can I define the type gracefully?
I would like to specify the type that some api have an e-mail in body and some api have a password in it.

Nodejs express app fails when using middleware on route

I am getting error Property 'admin' does not exist on type 'Request<{}, any, any, ParsedQs, Record<string, any>>'. Also nodejs app will not run as it fails with the same error. I am using typescript
app.get('/users', auth, (req, res) => {
console.log(` User is admin = ${req.admin}`);
console.log('Users Page');
res.send('Users Page');
});
function auth(req, res, next) {
if (req.query.admin === 'true') {
req.admin = true;
next();
} else {
res.send('No auth');
}
}
Make also sure that you properly structure the URL something like this http://localhost:3000/users?admin=true
import { NextFunction, Request, Response } from 'express';
app.get('/users', auth, (req: Request, res: Response) => {
console.log(` User is admin = ${req.admin}`);
console.log('Users Page');
res.send('Users Page');
});
declare module 'express-serve-static-core' {
export interface Request {
admin: boolean;
}
}
function auth(
req: Request<{}, {}, {}, {admin: boolean}>,
res: Response,
next: NextFunction) {
if (req.query.admin === 'true') {
req.admin = true;
next();
} else {
res.send('No auth');
}
}

req.user is undefined, using typescript

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();
}

How to get change request in nestjs middleware?

I am trying to integrate passport in NestJS and to get current auth info anywhere using a decorator. My NestJs version was so old, I updated the NestJS version and changed to the code below. After I changed middleware code, I can no longer get current auth info.
How to get auth info using the changed middeware?
Code before changing the middleware:
export class authMiddlware implements NestMiddleware {
async resolve(): Promise<MiddlewareFunction> {
return async (req, res, next) => {
passport.authenticate('jwt', { session: false }, (err, user, info) => {
if (err) {
return next(err);
}
if (user) {
req.user = user;
}
return next();
})(req, res, next);
};
}
After changing the middleware:
In passport.authenticate I can get the changed user from the request, but out of passport, I cannot get the user from the request.
#Injectable()
export class authMiddlware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
passport.authenticate('jwt', { session: false }, (err, user, info) => {
if (err) {
return next(err);
}
if (user) {
req.user = user;
}
console.log(req.user)
next();
})(req, res, next);
console.log(req.user)
};
}
The user.decorator:
export const passUser= createParamDecorator((data, req) => {
return plainToClass(User, req.user);
});

Passport JWT works with empty payload

I have initialized strategy for JWT:
const jwtStrategyOptions = {
jwtFromRequest: ExtractJwt.fromHeader('x-access-token'),
secretOrKey: 'publicKey',
}
passport.use(
new JwtStrategy(
jwtStrategyOptions,
(payload, done) => {
MySQL.Users.readOne(['id'], { id: payload.userId })
.fork(
error => {console.log(error)
done(error)},
user => {
console.log(user)
done(null, user)}
)
}
)
)
And middleware:
const isAuthenticated: RequestHandler = (req, res, next) => {
passport.authenticate(
'jwt',
{ session: false, failWithError: true },
(error, user) => {
//error is null when I pass empty payload
if (error) {
return next(error)
}
req.user = user
return next()
}
)(req, res, next)
}
But when I pass empty or invalid token Passport just pass this
(payload, done) => {
MySQL.Users.readOne(['id'], { id: payload.userId })
.fork(
error => {console.log(error)
done(error)},
user => {
console.log(user)
done(null, user)}
)
}
step and code execute next() function.
Can I somehow detect that payload is invalid or empty?
I'm not quite sure about the MySQL call return type, but if nothing matches the id, does it raise an error?
(payload, done) => {
MySQL.Users.readOne(['id'], { id: payload.userId })
.fork(
error => {console.log(error)
done(error)},
user => {
console.log(user)
done(null, user)}
)
}
If it doesn't raise an error but return null or empty value, you need to check it in the 'success' callback function, because in this case it will call done(null, user) with an empty value.
Based on your comment, this might help, some code that I was using to check for a token expiration error :
passport.authenticate('jwt',
{session: false},
//we need this callback to return information on why it's failing
//err is not populated, but 'info' is...
(err, user, info) => {
if (err) {
return next(err);
}
//if we couldn't authenticate the user, check why
//401 is used when no token or random information is provided
//403 is used when a well-formed token is provided, but it has expired thus not valid anymore
if (!user) {
if (info.name === 'TokenExpiredError') {
return res.status(403).send(info.name);
}
else {
return res.status(401).send(info.message);
}
}
req.user = user;
return next();

Resources