I am using Angular, Passport and sessions for authentication. I can create a new user, login but,
Problem
when checking if the user is logged using Passport's isAuthenticated() function always returns false. I followed the instructions from various online resources and I am unsure if:
Question
I can use passport and sessions with my Angular app? or I am just doing something wrong in my code? I would really appreciate it if you guys have any idea/hint why it's not working. Thank you.
Backend code
const cors = require('cors');
const express = require("express");
const app = express();
const PORT = 3000;
const mongoose = require('mongoose');
const seedDB = require("./helpers/dbseed");
const session = require("express-session");
const passport = require("passport");
const LocalStrategy = require("passport-local");
// the model with Passport-Local Mongoose plugged in
const User = require("./models/user");
const Profile = require("./models/profile");
const Recipe = require('./models/recipe');
const Macro = require ("./models/macro");
const Micro = require ("./models/micro");
const Ingredient = require("./models/ingredient");
// const {isLoggedIn} = require("./middleware/auth");
mongoose.connect('mongodb://localhost:27017/foodAppCopy', {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => {
console.log("Mongo Connection open");
})
.catch((error) => {
console.log("No, Mongo -> Connection Error " + error);
})
seedDB();
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.options('*', cors());
app.use(express.json());
const sessionConfig = {
secret: 'pass',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
expires: Date.now() + 1000 * 60 * 60 * 24 * 7,
maxAge: 1000 * 60 * 60 * 24 * 7,
}
}
app.use(session(sessionConfig));
app.use(passport.initialize());
app.use(passport.session());
// passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// Create New User
app.post('/register', async (req, res) => {
try {
const {name, email, password} = req.body;
const user = new User({name, email});
const registeredUser = await User.register(user, password);
// This is used to create a profile
res.send(registeredUser._id);
} catch(e) {
console.log(e.message);
}
});
// Login
app.post('/login', passport.authenticate('local', {successRedirect: '/recipes'}),
async(req, res) => {
});
// Create Profile
app.post('/profile', async(req, res) => {
const {userID, profileData} = req.body;
let user = await User.findOne({_id: userID});
if (user === null || user.length <= 0) {
return res.status(422).json("Could not create profile");
} else {
let newProfile = new Profile({
userID: user._id,
dob: profileData.dob,
gender:profileData.gender,
weightGoal:profileData.weightGoal,
weeklyLossFrequency:profileData.weeklyLossFrequency,
})
await newProfile.save();
}
});
// INDEX Recipes
app.get('/recipes', async (req, res) => {
if(!req.isAuthenticated()) {
console.log("error");
return res.status(422).json("You need to be logged in");
}
const recipes = await Recipe.find({});
res.send(recipes);
});
Middleware file
module.exports.isLoggedIn = (req, res, next) => {
if(req.isAuthenticated()) {
return next()
}
res.status(403).send("Please login");
}
[1]: https://i.sstatic.net/GsfG8.png
User Model
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
})
UserSchema.plugin(passportLocalMongoose, {
usernameField: "email"
});
module.exports = mongoose.model("User",UserSchema);
I'm just guessing what's wrong because you didn't precise the port of your application in your question, but I assume it's 4200 as I see that you have added these lines to the original project:
app.use(cors());
app.options('*', cors());
And you probably added them after having experienced this kind of error:
Access to fetch at 'http://localhost:3000/' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If my assumptions are right, then the problem you have is another problem related to your backend running on the port 3000 and the frontend running on the port 4200.
Indeed, you are using the session and you have configured it to use cookies. However, cookies won't be sent from another domain (different port is considered different domain) unless some special options are set:
access-control-allow-credentials: true
header in the HTTP responsewithCredentials
option in the HTTP requestSameSite=none
cookie attributeThese options tend to lower the security level of your website (other CORS options do as well). The easiest solution to stay at a good security level and avoid those errors is to serve the frontend and the backend on the same port.
On the local environment, this can be done with the Angular development proxy which you could configure like this:
{
"/backend": {
"target": "http://localhost:3000",
"secure": false,
"pathRewrite": {
"^/backend": ""
}
}
}
Don't forget the reference to that new file in the angular.json
file:
"proxyConfig": "src/proxy.conf.json"
After that, change all of your backend calls from http://localhost:3000/...
to http://localhost:4200/backend/...
and your login should work fine (you can remove the two CORS lines as well).