2
votes

I have a React App that is calling express API which authenticates with a local strategy using Passport JS.

The client login page calls the /auth/login route successfully. req.user is sent back to the client, and the serialize function is called (I have a console.log that prints).

Router.post('/login', passport.authenticate('local'), (req, res) => {
    res.send(req.user);
})

Subsequent requests to get user info from /auth/instructor don't work. req.user returns undefined and deserializeUser() is never called.

Router.get('/instructor', (req,res)=>{
    console.log(req.user)
    if(req.user) res.send(req.user)
    else res.send({username: "No user logged in"})
})

I believe my passport and session config is setup correctly.

const passportLocal = require('passport-local').Strategy
const bcrypt = require('bcryptjs')
const instructor = require('../Models/instructor.model')

module.exports = function(passport){
    
    const strategy = new passportLocal( (username, password, done)=> {
        console.log(username, password)
        instructor
            .findOne({username})
            .then( instructor => {
                if (!instructor) return done(null, false)
                if (bcrypt.compareSync(password, instructor.password)) return done( null, instructor)
                return (null, false)
            })
            .catch( error => console.log(error))
    })

    passport.use(strategy)

    passport.serializeUser( (user, done)=> {
        console.log('serialize', user._id)
        done(null, user._id)
    })

    
    passport.deserializeUser( (id, done)=> {
        console.log('de-serialize', id)
        instructor.findById(id).then( instuctor => done( null, instructor))
    })
}

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const cors = require('cors')
const session = require('express-session')
const passport = require('passport')

// Import routers
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
const pupilsRouter = require('./routes/pupils.routes');
const authRouter = require('./routes/auth.routes');


// Connect to database
require('./config/mongoose.config.js');


var app = express();


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser("secret"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(session({
    secret: "secret",
    resave: true, 
    saveUninitialized: true, 
    cookie: {secure: false}
}))


app.use(cors({
    origin: "http://localhost:3003",
    credentials: true,
}))





// Initialize Passport Middleware
app.use(passport.initialize())
app.use(passport.session())
require('./config/passport.config')(passport)


// Use Routers
app.use('/api', indexRouter);
app.use('/api/users', usersRouter);
app.use('/api/pupils', pupilsRouter);
app.use('/api/auth', authRouter);
app.get('/api/ping', (req, res) => res.send("server is working"))

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

const Router = require('express').Router()
const Instructor = require('../Models/instructor.model');
const bcrypt = require('bcryptjs');
const passport = require('passport');

Router.post('/register', (req, res) => {
    Instructor.findOne({username: req.body.username}).then( results => {
        if (!results) {
            const instructorData = {...(req.body), password: bcrypt.hashSync(req.body.password) }
            Instructor.create(instructorData).then(console.log)
            res.send("Instructor registered")
        } else {
            res.status(400).send("Instructor Already Exists")
        }
    } )

    
})

Router.post('/login', passport.authenticate('local'), (req, res) => {
    res.send(req.user);
})

Router.get('/instructor', (req,res)=>{
    console.log(req.user)
    if(req.user) res.send(req.user)
    else res.send({username: "No user logged in"})
})
 
module.exports = Router

This is the client code I'm trying to get to work. Clicking the profile button should get the user from the session and display the username.

import React, { useState, useEffect } from 'react'
import { NavLink} from 'react-router-dom'
import styled from 'styled-components';
import axios from 'axios'


import Logo from './Logo'
axios.defaults.withCredentials = true;
function Header() {

    const [user, setUser] = useState({})
    useEffect( ()=>{
        axios
            .get( 'http://localhost:3000/api/auth/instructor')
            .then( response => setUser( response.data ))
           
    } ,[])

    const getUser = ()=> {
        axios
        .get( 'http://localhost:3000/api/auth/instructor')
        .then( response => setUser( response.data ))
    }


    return (
        <StyledDiv>
            <Logo/>
            <div className="auth">
                <NavLink to="/auth/login"> Login </NavLink>
                <NavLink to="/auth/register"> Register </NavLink>
                <button className="profile-button" onClick={getUser}>{user.username || "Get User"}</button>
            </div>

        </StyledDiv>
    )
}


 const StyledDiv = styled.div`
    width: 100vw; height: 90px; 
    display: grid; place-content: center;
    position: relative;
    box-shadow: 0px 3px 5px 5px lightgray, 0px 2px 1px 1px black;
    
    div.auth {
        position: absolute;
        right: 50px; top: 35px;

        a {
            margin-right: 7px;
        }
    }

    .user-profile {
        background: purple;
        color: white;
    }
 
 `

export default Header

Checking the network requests, the client side seems to be setting the SID cookie and making the cors request with no issue. But the req.user has no data..

Screenshot of network request

Appreciate any help here, I'm going mad.

1

1 Answers

0
votes

It looks like there is a typo (instuctor) in your callback function.

Fix:

passport.deserializeUser( (id, done)=> {
        console.log('de-serialize', id);
        instructor.findById(id).then((instructor) => done( null, instructor));
    })

If you are using VS Code I would also recommend a spell checker plugin.