Home > Software design >  Passport JS Local Strategy not being called at all, login and authentication only working for one mo
Passport JS Local Strategy not being called at all, login and authentication only working for one mo

Time:07-14

I have been struggling with this issue for more than a day now. I am building a backend API for a food delivery web and mobile app.

I have setup passport with the local strategy to authenticate my users.

I have two different types of users currently 1. Subscribers 2. Restaurants each using their own mongoose model (shown in my code below)

I initially set everything up for subscriber registration login, authentication sessions and cookies, all was working fine.

I then moved on to setting up passport for my Restaurant users so that a a restaurant would be able to login to their profile and make various changes to their restaurant and its listings etc.

What I found was that I was able to create an account for the Restaurant user but when trying to log the user in passport would consistently return "Unauthorized", when trying to us isAutheticated() I got the same response back. Passport would however happily keep creating restaurant users every time I hit my register endpoint. A Restaurant user would appear in my database but when trying to log them in and store a cookie, I would just get returned "unauthorized".

With Subscribers all works fine, I have check authentication on my restricted endpoints, register new Subscribers and login subscribers with 0 issues.

Through my testing I have realized a couple things

  1. Passport doesn't make use of the local strategy at all, I can literally black the whole thing out and it will still register subscribers log them in and check authentication via cookies with 0 issues. When placing a console.log into the strategy I can see the strategy is literally not being called at all. How could this be happening, surely passport needs the strategy in order to do its job? In this case its seems not, WHY?

  2. Initially I tried to create a separate strategy for each of my Models, one strategy for Subscribers and one strategy for Restaurants using .createStrategy() this returned the same issue, all functionality for Subscriber model users worked fine but as soon as I tried to login or store a cookie for a user from my Restaurant model passport would just return "Unauthorized. Then you will see in the below code under server.js that I tried to create one local strategy and then build an if else in so passport checks both Subscriber and Restaurant models, through doing this I realized passport wasn't using the strategy at all.

I am ready to tear my hair out at this point what is the point of creating a strategy is passport is somehow just bypassing it completely. If I try to log a Subscriber in with the wrong credentials passport does its job and doesn't allow access so how is it actually managing authentication?

Below is my code, anyone who could help with this would more than make my day!

Server.js:

require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const app = express();
const cors = require("cors")
const bodyParser = require("body-parser");
const passport = require("passport");
const session = require("express-session");
const LocalStrategy = require('passport-local').Strategy;



mongoose.connect(process.env.DATABASE_URL)

const db = mongoose.connection



db.on("error", () => console.error(error))
db.once("open", () => console.log("connected to database"))

// app.use(express.json())


// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
    // console.log(req.originalUrl)
    if (req.originalUrl === "/subscribers/webhook") {
        next();
    } else {
        bodyParser.json()(req, res, next);
    }
});
app.use(cors())

//INITIAL STRATEGY i TRIED

// passport.use(new LocalStrategy(
//     function (username, password, done) {
//         Subscriber.findOne({
//             username: username
//         }, function (err, user) {
//             if (err) {
//                 return done(err);
//             }
//             if (!user) {
//                 return done(null, false);
//             }
//             if (!user.verifyPassword(password)) {
//                 return done(null, false);
//             }
//             return done(null, user);
//         });
//     }
// ));

app.use(session({
    secret: "foodsecrets",
    resave: false,
    saveUninitialized: false
}));


app.use(passport.initialize());
app.use(passport.session());


// sTRATEGY i WROTE TO TRY SEPERATE THE MODELS BEFORE I REALIZED THE STRATEGY WAS NOT BEING CALLED AT ALL
passport.use(new LocalStrategy(function (username, password, done) {
    Subscriber.findOne({
        username: username,
        password: password
    }, function (err, user) {
        console.log("called")
        // first method succeeded?
        if (!err && user && passwordMatches(password)) {

            return done(null, user);
        }
        // no, try second method:
        Restaurant.findOne({
            name: username,
            password: password
        }, function (err, user) {
            // second method succeeded?
            
            if (!err && user && passwordMatches(password)) {
                return done(null, user);
            }
            // fail! 
            done(new Error('invalid user or password'));
        });
    });
}));




const subscribersRouter = require("./routes/subscribers")
const restaurantsRouter = require("./routes/restaurants")
const ordersRouter = require("./routes/orders")
const seederRouter = require("./routes/seeder");







app.use("/subscribers", subscribersRouter)
app.use("/restaurants", restaurantsRouter)
app.use("/orders", ordersRouter)
app.use("/seeder", seederRouter)








app.listen(3000, () => {
    console.log("Server has started on port 3000")
});

Subscriber Model:

const mongoose = require("mongoose")
const passportLocalMongoose = require("passport-local-mongoose");


const cartSchema = new mongoose.Schema({
    name: {
            type: String,
            required: true
        },
        price: {
            type: Number,
            required: true

        },
        description: {
            type: String,
            required: true
        },
        categories: {
            type: Array,
            required: true

        },
        rating: {
            type: String

        },

        restaurantname: {
            type: String
        }
    
});

const favouritesSchema = new mongoose.Schema({
    favouritemeals: {
        type: Array

    },
    favouriteresturants: {
        type: Array

    }
});

const pendingItemsSchema = new mongoose.Schema({
    name: String,
    price: Number,
    description: String

});


const pendingOrderSchema = new mongoose.Schema({
     userID: {
             type: String,
             required: true
         },
         total: {
             type: Number,
             required: true
         },
         items: [pendingItemsSchema],
         removeItem: {
             type: String
         },
         orderData: {
             type: Date,
             required: true,
             default: Date.now
         },
         status: {
             type: String
         }
});

const subscriberSchema = new mongoose.Schema({
    googleID: {
        type: String
    },
    facebookID: {
        type: String
    },
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
    },
    subscribeData: {
        type: Date,
        required: true,
        default: Date.now
    },
    orderHistory: {
        type: Array,
    },
    favourites: {
        favouritesSchema
    },
    cart: [cartSchema],
    login: {
        type: String,

    },
    pendingOrder: [pendingOrderSchema],
    stripeCustId: {
        type: String,
        required: true
    },
    role:{
        type: String,
        // required: true

    }
});



subscriberSchema.plugin(passportLocalMongoose);


module.exports = mongoose.model("subscriber", subscriberSchema);

Subscribers.js all required

const express = require("express");
const router = express.Router();
const Subscriber = require("../models/subscriber");
const Restaurant = require("../models/restaurant");
const passport = require("passport");
const Order = require("../models/order");
const bodyParser = require("body-parser");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const facebookStrategy = require('passport-facebook').Strategy;
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET




// passport.use(Subscriber.createStrategy());




passport.serializeUser(function (user, done) {
    done(null, user.id);
});

passport.deserializeUser(function (id, done) {
    Subscriber.findById(id, function (err, user) {
        done(err, user);
    });
});

Subscribers.js Login endpoint:

// LOGIN USING PASSPORT JS 
router.post("/login", (req, res) => {
    const subscriber = new Subscriber({
        username: req.body.username,
        password: req.body.password,
        email: req.body.email
    });
    req.login(subscriber, async function (err) {
        if (err) {
            console.log(err)
        } else {
            try {
                passport.authenticate("LocalStrategy")(req, res, function () {
                    console.log("Authenticated")
                    
                    res.status(201).json("authenticated")

                })
            } catch (err) {
                res.status(400).json({
                    message: err.message
                })
            }
        }
    })

})

Subscribers.js Register Endpoint:

// REGISTER USING PASSPORT JS
router.post("/register", async (req, res) => {

    const customer = await stripe.customers.create({
        name: req.body.username,
        email: req.body.email
    });

    console.log("customer", customer)

    Subscriber.register({
        username: req.body.username,
        email: req.body.email,
        stripeCustId: customer.id

    }, req.body.password, async (err, subscriber) => {
        if (err) {
            console.log(err)
        } else {
            try {
                await passport.authenticate("local")(req, res, function () {

                    console.log("is authenticated")
                    res.status(201).json(newSubscriber)

                })
                const newSubscriber = await subscriber.save()

            } catch (err) {
                res.status(400).json({
                    message: err.message
                })
            }
        }
    });
})

Restaurant Model:

const mongoose = require("mongoose")
const passportLocalMongoose = require("passport-local-mongoose");

const menueItemSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true

    },
    description: {
        type: String,
        required: true
    },
    categories: {
        type: Array,
        required: true

    },
    rating: {
        type: Number

    },
    restaurantname: {
        type: String
    }
})

const activeOrderSchema = new mongoose.Schema({
    userID: {
            type: String,
            required: true
        },
        total: {
            type: Number,
            required: true
        },
        items: [menueItemSchema
        ],
        orderData: {
            type: Date,
            required: true,
            default: Date.now
        },
        status: {
            type: String
        }
})


const restaurantSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
    },
    src: {
        type: String,
        required: true

    },
    title: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true

    },
    menue: [menueItemSchema],
    rating: {
        type: String
    },
    categories: {
        type: String
    },
    subscribeData: {
        type: Date,
        required: true,
        default: Date.now
    },
    activeOrders: [activeOrderSchema]

})

restaurantSchema.plugin(passportLocalMongoose);

module.exports = mongoose.model("restaurant", restaurantSchema)

Restaurants.js all required:

const express = require("express")
const router = express.Router()
const Restaurant = require("../models/restaurant")
const passport = require("passport");
const randomRest = require("randomrestgenerator")



// passport.use(Restaurant.createStrategy());

passport.serializeUser(function (user, done) {
    done(null, user.id);
});

passport.deserializeUser(function (id, done) {
    Restaurant.findById(id, function (err, user) {
        done(err, user);
    });
});

Restaurants.js Login endpoint:

router.post("/login", (req, res) => {
    const restaurant = new Restaurant({
        username: req.body.username,
        password: req.body.password,
        email: req.body.email
    });
    req.login(restaurant, async function (err) {
        if (err) {
            console.log(err)
        } else {
            try {
                passport.authenticate("local")(req, res, function () {
                    console.log("Authenticated")

                    res.status(201).json("authenticated")

                })
            } catch (err) {
                res.status(400).json({
                    message: err.message
                })
            }
        }
    })

})

Restaurants.js Register endpoint

// PASSPORT JS RESTAURANT REGISTRATION
router.post("/register", async (req, res) => {

    const randomRestaurant = randomRest()


Restaurant.register({
    username: req.body.username,
    email: req.body.email,
    src: randomRestaurant.img,
        title: randomRestaurant.title,
        description: randomRestaurant.description,
        menue: randomRestaurant.menue,
        rating: randomRestaurant.rating,
        categories: randomRestaurant.categories

}, req.body.password, async (err, restaurant) => {
    if (err) {
        console.log(err)
    } else {
        try {
            console.log("try called")
             const newRestaurant = await restaurant.save()
            await passport.authenticate("rest")(req, res, function () {
                
                console.log("is authenticated")
                res.status(201).json(newRestaurant)

            })
           

        } catch (err) {
            console.log("here!")
            res.status(400).json({
                message: err.message
            })
        }
    }
});

CodePudding user response:

I managed to fix this a couple days ago, turns out I had left my JSON response outside of the passport.authenticate callback function in the restaurants login endpoint.

I moved serialize and deserialize into server.js as well as my session and passport initialization and passport session .use

Also had to setup my serialize and desearialize user functions with if elses' so that they serviced both models.

Then lastly I added an array of secrets to my session instead of just one string.

All is working fine now.

passport.serializeUser(function (user, done) {
    if (user instanceof Subscriber) {
        done(null, {
            id: user.id,
            type: "Subscriber"
        });
        console.log("sub user")
    } else {
        console.log("rest user")
        done(null, {
            id: user.id,
            type: "Restaurant"
        })
    }


});

passport.deserializeUser(function (id, done) {
    console.log("de-serialize called")
    console.log("id type", id.type)
    console.log("ID", id)
    if (id.type === "Subscriber") {
        Subscriber.findById(id.id, function (err, user) {
            done(err, user);
        })
    } else {
        Restaurant.findById(id.id, function (err, user) {
            done(err, user);
        })
    }

});
  • Related