Next.js and Mongoose and Passport.js: req.user is undefined - mongodb

I am using the relatively newer setup with Next.js API routes, this doesn't use express but instead the API setup to create your routes to an endpoint.
My problem is I'd like to have a route called api/user which will return the user i.e. req.user:
The following comes from with-passport-and-next-connect
import nextConnect from 'next-connect'
import auth from '../../middleware/auth'
const handler = nextConnect()
handler
.use(auth)
.get((req, res) => {
if (req.session.user) {
const { _id } = req.session.user
res.json({ user: { _id } })
} else {
res.json({ user: null })
}
})
export default handler
That file references this auth file:
import nextConnect from 'next-connect'
import passport from '../lib/passport'
import session from '../lib/session'
const auth = nextConnect()
.use(
session({
name: 'sess',
secret: process.env.TOKEN_SECRET,
cookie: {
maxAge: 60 * 60 * 8, // 8 hours,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
},
})
)
.use((req, res, next) => {
// Initialize mocked database
// Remove this after you add your own database
req.session.users = req.session.users || []
next()
})
.use(passport.initialize())
.use(passport.session())
export default auth
I have added this in my version:
.use((req, res, next) => {
connectDB()
next()
})
But that yields nothing.
In api/login I added this line
req.session.user = req.user;
after the authenticate function. But that doesn’t add the user to the session object.
handler
.use(auth)
.post((req, res, next) => {
emailValidator(req, res, next, 'email', 'password');
},
async (req, res, next) => {
await connectDB();
passport.authenticate('local', (err, user, info) => {
req.session.user = req.user;
Could anyone please help me with this?
Thanks in advance.

Related

crbug/1173575, non-JS module files deprecated VM10:6747

Hello I am super new to web development, currently doing the udemy bootcamp. I was working on my app and everything was fine but suddenly my browser stopped rendering and I got this error:
VM10:6747 crbug/1173575, non-JS module files deprecated.
Got the error even though it shows in my terminal that the database is connected and working. I googled and youtubed this problem but none of the solutions online seem to work. I am using VSC and I went ahead and deleted the launch.json file but that didnt work either. Any other suggestions would really be appreciated. Is there anything within my code that can produce this error? Here is my app.js in case I am missing something.
const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
const ejsMate = require('ejs-mate'); //this is an engine use to parse ejs
const session = require('express-session');
const flash = require('connect-flash');
const ExpressError = require('./utils/ExpressError');
const methodOverride = require('method-override');
//requiring routes
const campgrounds = require('./routes/campgrounds');
const reviews = require('./routes/reviews');
mongoose.set('strictQuery', true);
mongoose.connect('mongodb://127.0.0.1:27017/yelp-camp', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
console.log("Database connected")
})
.catch(err => {
console.log("Database not connected")
console.log(err)
});
const app = express();
app.engine('ejs', ejsMate); //here we are telling express to use the engine we selected instead of the default one
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.urlencoded({ extended: true })); //to help us parse the req.body
app.use(methodOverride('_method'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(flash);
const sessionConfig = {
secret: 'thisshouldbeabettersecret',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true, //this is a security item
expires: Date.now() + 1000 * 60 * 60 * 24 * 7, //date.now is in miliseconds. the numbers after are a multiplication = to a week
maxAge: 1000 * 60 * 60 * 24 * 7
}
}
app.use(session(sessionConfig));
app.use((req, res, next) => {
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
app.use('/campgrounds', campgrounds);
app.use('/campgrounds/:id/reviews', reviews);
app.get('/app', (req, res) => {
// res.send('hello! working on your request')
res.render('home')
})
// HOME PAGE
app.get('/', (req, res) => {
res.send('HOMEPAGE!');
})
//ERROR MIDDLEWARE
app.all('*', (req, res, next) => {
next(new ExpressError('Page not found', 404))
}) //this selects all and if nothing else wrongs this will.
app.use((err, req, res, next) => {
const { statusCode = 500 } = err; //here we are deconstructing our expresserror and then sending the message
if (!err.message) err.message = 'Oh no, Something Went Wrong';
res.status(statusCode).render('error', { err })
})
app.listen(3000, () => {
console.log("Serving in port 3000")
})
Nevermind! Found the error.
Had:
app.use(flash)
Instead of:
app.use(flash())

Persist session id in passport-saml login login callback

I'm using passport-saml and express-session. I login with my original session id but when the idp response reach the login callback handler, I have another sessionId. Also, since my browser has the session cookie with the original session id, it cannot use the new session id in the login callback, so I cannot authenticate.
interface SamlProvider {
name: string;
config: SamlConfig;
}
const providers: SamlProvider[] = [
{
name: process.env.SAML_ENTITY_ID_1!,
config: {
path: "/login/callback",
entryPoint: process.env.SAML_SSO_ENDPOINT_1,
issuer: process.env.SAML_ENTITY_ID_1,
cert: process.env.SAML_CERT_1!,
...(process.env.NODE_ENV === "production" && { protocol: "https" }),
disableRequestedAuthnContext: true,
},
},
{
name: process.env.SAML_ENTITY_ID_2!,
config: {
path: "/login/callback",
entryPoint: process.env.SAML_SSO_ENDPOINT_2,
issuer: process.env.SAML_ENTITY_ID_2,
cert: process.env.SAML_CERT_2!,
...(process.env.NODE_ENV === "production" && { protocol: "https" }),
disableRequestedAuthnContext: true,
},
},
];
export const samlStrategy = (sessionStore: session.Store) =>
new MultiSamlStrategy(
{
passReqToCallback: true, // makes req available in callback
getSamlOptions: function (request, done) {
// Find the provider
const relayState = request.query.RelayState || request.body.RelayState;
const provider = providers.find((p) => p.name === relayState);
if (!provider) {
return done(Error("saml identity provider not found"));
}
return done(null, provider.config);
},
},
async function (
req: Request,
profile: Profile | null | undefined,
done: VerifiedCallback
) {
if (profile && profile.nameID) {
const { nameID, nameIDFormat } = profile;
const email = profile[
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
] as string;
const firstName = profile[
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
] as string;
const lastName = profile[
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
] as string;
// Check if user is in risk database
const user = await myUserService.getByEmail(email);
if (!user) return done(new UserNotFoundError());
// If user has existing session, delete that existing session
sessionStore.all!((err: any, obj: any) => {
const sessions = obj as Array<{
sid: string;
passport?: { user?: { email?: string } };
}>;
const existingSess = sessions.find(
(sess) =>
sess.passport &&
sess.passport.user &&
sess.passport.user.email &&
sess.passport.user.email === email
);
if (existingSess && existingSess.sid) {
sessionStore.destroy(existingSess.sid, (err: any) => {
console.error(err);
return done(Error("failed to delete existing user session"));
});
}
});
return done(null, { nameID, nameIDFormat, email, firstName, lastName });
}
return done(Error("invalid saml response"));
}
);
Here's my login and login callback
app.post("/login/callback", async function (req, res, next) {
passport.authenticate("saml", (err: any, user: ISessionUser) => {
if (err) {
// TODO: Handle specific errors
logger.info({ label: "SAML Authenticate Error:", error: err });
return next(err);
} else {
req.logIn(user, (err) => {
if (err) {
logger.info({ label: "Login Error:", data: err });
return next(err);
}
res.redirect("/");
});
}
})(req, res, next);
});
app.get(
"/auth/saml/login",
passport.authenticate("saml", { failureRedirect: "/", failureFlash: true }),
function (req, res) {
res.redirect("/");
}
);
I experienced a similar issue using Microsoft 365 for authentication. The answer was to pass a randomly-generated nonce to the authentication request - this gets passed back to your app in the callback request. With SAML I think it depends on the provider whether they support such a flow, but it is good practice. You can also use a cookie to maintain state in your app, instead of, or additional to, the session id.

Setup error handlers in express/mongoose/mongoDB

I'm currently part of a web dev Bootcamp and my current project is requesting I create error handlers in a specific manner that I do not understand. Below is a screenshot of the directions . . .
Here are my current files in hopes that it makes sense . . .
/* app.js */
const express = require('express');
const mongoose = require('mongoose');
const userRouter = require('./routes/users');
const cardRouter = require('./routes/cards');
const { PORT = 3000 } = process.env;
const app = express();
mongoose.connect('mongodb://localhost:27017/aroundb', {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use((req, res, next) => {
req.user = { _id: '60c4e0e2a80be4c8c2de5474' };
next();
});
app.use('/users', userRouter);
app.use('/cards', cardRouter);
app.listen(PORT, () => logMsg(`listening on port ${PORT} . . .`));
/* routes/users.js */
const express = require('express');
const { getUsers, getUser, createUser } = require('../controllers/users');
const router = express.Router();
router.get('/', getUsers);
router.get('/:id', getUser);
router.post('/', createUser);
module.exports = router;
/* controllers/users.js */
const User = require('../models/user');
module.exports.getUsers = (req, res) => {
User.find({})
.then((users) => res.status(200).send({ data: users }))
.catch((err) => res.status(500).send({ message: err }));
};
module.exports.getUser = (req, res, next) => {
User.findById(req.params.id)
.then((user) => res.send({ data: user }))
.catch((err) => res.status(404).send({ message: err }));
};
module.exports.createUser = (req, res) => {
const { name, about, avatar } = req.body;
User.create({ name, about, avatar })
.then((user) => res.status(201).send({ data: user }))
.catch((err) => res.status(400).send({ message: err }));
};
My questions are:
Where should the code example provided (in the screenshot) go? Am I creating a separate controller or middleware? Or maybe it goes in the already coded controller?
Would I be creating my own errors and using a conditional to read the message?
I already thought I was handling errors, as seen in controllers/users.js, is that not the case?
NOTE: My apologies, I know that since it's from a course it might not make sense outside the context of the lesson(s). I also know there are various ways projects can be coded/solved. Unfortunately, my Bootcamp does not have live instruction, just a slack channel where 97% of responses come from Alumni. Please do not hesitate to ask questions that may help clarify things.
It seems you're directly sending an error in the last two cases, without knowing which type of it is, however it looks fine for fetching all users (1st case).
The workaround that might help you is,
Get user :
User.findById(req.params.id), function(err, user) {
if(err) {
return res.status(500).send({ message: "Default error" });
} else if (!user) {
return res.status(404).send({ message: "User not found" });
}
}
For creating a user you need to manually verify all the fields that are required in schema for ex.,
createUsers : {
const { name, about, avatar } = req.body;
if (name === null || about === null || avatar === null) {
return res.status(400).send({
message : "Required data missing in request"
})
}
... // create user
}

Got an error in Express, cant send request

controllers/userController.js
import User from '../models/userModel.js'
import asyncHandler from 'express-async-handler'
import generateToken from '../utils/generateToken.js'
// #desc Auth user & get token
// #route POST /api/users/login
// #access Public
const authUser = asyncHandler(async(req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if(user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id)
})
} else {
res.status(401)
throw new Error('Invalid email or Password')
}
})
// #desc Get user Profile
// #route GET /api/users/login
// #access Private
const getUserProfile = asyncHandler(async(req, res) => {
// res.json(req.user)
const user = await User.findById(req.user._id)
console.log('user', user)
if (user) {
res.json(user)
} else {
res.status(404)
throw new Error('User not Found')
}
})
export { authUser, getUserProfile }
middleware/errorMiddleWare.js
const notFound = (req, res, next) => {
const error = new Error(`Not Found - ${req.originalUrl}`)
res.status(404)
next(error)
}
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode
res.status(statusCode)
res.json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? null : err.stack
})
}
export { notFound, errorHandler }
middleware/authMiddleware.js
import jwt from 'jsonwebtoken'
import asyncHandler from 'express-async-handler'
import User from '../models/userModel.js'
const protect = asyncHandler(async(req, res, next) => {
let token
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
try {
token = req.headers.authorization.split(' ')[1]
const decoded = await jwt.verify(token, process.env.JWT_SECRET)
req.user = await User.findById(decoded.id).select('-password')
next()
} catch (error) {
res.status(401)
throw new Error('Not Authorized, token failed')
}
}
if(!token) {
res.status(401)
throw new Error('Not Authorized')
}
next()
})
export { protect }
routes/userRoutes.js
import express from 'express'
const router = express.Router()
import { authUser, getUserProfile } from '../controllers/userController.js'
import { protect } from '../middleware/authMiddleware.js'
router.post('/login', authUser)
router.route('/profile').get(protect, getUserProfile)
export default router
I got an error in userController.js, error from my errorMiddleware.
Scenario :
If I send a response from "if statement". (after User.findById)
But if I send response before "if statement", it work (is not Good). But why? and how can I solve this (to send a response after using User.findById) ?
I got an Error in server console when I used scenario 1 or 2.
version
node 14.12.0
express 4.17.1
Done, I forgot to delete next() in middleware/authMiddleware.js to protect getUserProfile.

DigestAuth in Sails v0.11

Trying to integrate digest auth in sails. Using passport-http module for it . https://github.com/jaredhanson/passport-http
This is the policy I have defined.
module.exports = function(req, res, next) {
var passport = require('passport');
var Strategy = require('passport-http').DigestStrategy;
passport.use(new Strategy({ qop: 'auth' },
function(username, cb) {
console.log("in strategy");
user = { id: 1, username: 'postman', password: 'password', displayName: 'Postman', emails: [ { value: 'postman#example.com' } ] }
if(username == user.username) {
return cb(null,user,user.password)
}
else{
return cb(null,false)
}
}
));
passport.authenticate('digest',{session: false});
next();
};
Now in express the passport.authenticate function returns in case of error but in case of sails , it does not return here as a middle-ware. Please point me in the right direction , spent a lot of time on this .
I want to basically return a not authorized in case of wrong headers else move on to the controller action.