Click here to Skip to main content
15,885,757 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am developing an ecommerce as a MERN fullstack exercise and I am having this problem. The particular thing is that the error occurs only in this condition: if I start the application while not logged in and then I log in (if I do the console.log of the token, saved in the cookies, it returns null). If I refresh the page everything will work again in this case as well.

I tried to verify the tokens on jwt.io and they are correct.

This is the code of the sign in and sign in with Google controller part, where I create the token:

JavaScript
import validator from 'validator';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import {OAuth2Client} from 'google-auth-library';
import sgMail from '@sendgrid/mail';
import dotenv from 'dotenv';
import {User} from '../models/User.js';

//LOADING ENVIRONMENT VARIABLE
dotenv.config();

const JWT_CONFIRM_ACCOUNT = process.env.JWT_CONFIRM_ACCOUNT;
const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRE = process.env.JWT_EXPIRE;
const CLIENT_URL = process.env.CLIENT_URL;
const SGMAIL_API_KEY = process.env.SGMAIL_API_KEY;
const JWT_RESET_PASSWORD = process.env.JWT_RESET_PASSWORD;
const JWT_EMAIL_FROM = process.env.JWT_EMAIL_FROM;

//SETTING SGMAIL APIKEY
sgMail.setApiKey(SGMAIL_API_KEY);

//SIGN-IN
export const signIn = async (req, res) => {
    const { email, password } = req.body;

    try {
        const user = await User.findOne({email});

        if(!user){
            return res.status(400).json({errorMessage : 'Invalid credentials'})
        }

        if(!email || !password){
            return  res.status(400).json({errorMessage: 'All fields are required'})
         }
        
         if(!validator.isEmail(email)){
            return res.status(400).json({errorMessage : 'Invalid email'})
        }

           
        //JWT
        const payload = {
            user : {
                _id : user._id
            }
        }

        const isMatch = await bcrypt.compare(password, user.password);
        if(!isMatch){
            return res.status(400).json({errorMessage: 'Invalid credentials'})
        }
        else{
            jwt.sign(
                payload, 
                JWT_SECRET,
                {expiresIn: JWT_EXPIRE},
                (err, token) => {
                    if(err) console.log ('Jwt error: ', err);
    
                    const {_id, username, email, role} = user;
    
                    res.json({
                        token,
                        user : {_id, username, email, role}
                    })
                }
            )

        }
  
    } catch (error) {
       return  res.status(500).json({errorMessage : `${error}`})
    }
}

//GOOGLE LOGIN

export const googleLogin =  (req, res) => {

    const {token} = req.body;

    const client = new OAuth2Client('203822999175-ic2e949ccriqa983otoa5949dodi7iif.apps.googleusercontent.com');
    client.verifyIdToken({idToken: token, audience: '203822999175-ic2e949ccriqa983otoa5949dodi7iif.apps.googleusercontent.com'})
    .then(response => {
        const{email_verified, name, email, picture: userImage, given_name: firstName, family_name: lastName} = response.payload;
    

        if(email_verified){
            User.findOne({email}).exec((err, user) => {
                if(user){
                    user.image = userImage;
                    const payload =  {user : {
                        _id : user._id
                    }}
                    const token = jwt.sign( payload, JWT_SECRET, {expiresIn: JWT_EXPIRE});
                    const {_id, email, username, role} = user;
                    return res.json({
                        token,
                        user: {_id, email, username, role, userImage, firstName, lastName}
                    })
                }else{
                    let password = email + JWT_SECRET;
                    user = new User({
                        username: firstName, email, password, image: userImage
                    });
                    user.save((err, save) => {
                        if(err){
                            return res.status(400).json({errorMessage: `${err}` })
                        }
                        const token = jwt.sign({_id: user._id}, JWT_SECRET, {expiresIn: JWT_EXPIRE});
                        const {_id, email, username, role} = user;

                        const emailData = {
                            from: JWT_EMAIL_FROM,
                            to: email,
                            subject: "Account created - Tea Store",
                            html: `
                                <h1>Hi ${user.username}! Your account has been created</h1>
                                <p style = {{fontWeight : 'bold'}}>Thanks for logging in with google. 
                                To access the site via our custom sign-in form, choose a password for your account at the following link:</p>
                                <hr/>
                                <p>${CLIENT_URL}</p>
                            `
                        }

                        sgMail.send(emailData, (err, sent) => {
                            if(err){
                                return res.json({errorMessage: `${err}`})
                            }
                        })

                        return res.status(200).json({
                            token, 
                            user: {_id, email, username, role, userImage, firstName, lastName},
                            successMessage: 'Account successfully created! Check your mailbox'
                        })
                    })
                }
            })
        }else{
            return res.status(400).json({errorMessage: 'Google login failed'})
        }
    }).catch(err => {
        return res.status(500).json({errorMessage: 'Server Error'})
    })

}


Code of the sign in and sign in with Google route part:

JavaScript
import express from 'express';
import {signUp, signIn, googleLogin, forgotPassword, resetPassword, accountAuth} from '../controllers/auth.js';


const router = express.Router();

router.route('/sign-up').post(signUp);
router.route('/account-authentication/:token').post(accountAuth);
router.route('/sign-in').post(signIn);
router.route('/google-login').post(googleLogin);
router.route('/forgot-password').post(forgotPassword);
router.route('/reset-password/:token').post(resetPassword);

export default router;



Server file:

JavaScript
import express from 'express';
import mongoose from 'mongoose';
import cors from 'cors';
import dotenv from 'dotenv';
import authRouter from './routes/auth.js';
import categoryRouter from './routes/category.js';
import productRouter from './routes/product.js';


//DOTENV CONFIG
dotenv.config();
const MONGODB_CONNECTION = process.env.MONGO_URI;

//INITIALIZE APP
const app = express()

//MIDDLEWARES
/*app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "http://localhost:3000");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
  });*/
  
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(cors({
    origin: 'http://localhost:3000',
    methods: ['GET','POST','DELETE','UPDATE','PUT','PATCH']
}));



//ROUTES
app.use('/api/auth', authRouter);
app.use('/api/categories', categoryRouter);
app.use('/api/products', productRouter)


//PORT
const PORT = process.env.PORT || 5020


//CONNECT MONGODB
const connectDB = () => {
    mongoose.connect(MONGODB_CONNECTION, {
        useNewUrlParser: true,
        useFindAndModify: true,
        useUnifiedTopology: true,
        useCreateIndex: true
    } )
        .then(() => console.log('MONGODB is connected'))
        .catch(err => console.log('MONGODB connection error:', err ))
}

connectDB();

//INITIALIZE SERVER
app.listen(PORT, () => console.log (`Connection is established and running on port ${PORT}`)
)



Authorization middlewares:

JavaScript
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
import {User} from '../models/User.js';

dotenv.config();
const JWT_SECRET = process.env.JWT_SECRET;


export const authMiddleware = async (req, res, next) => {

    try {

        let token;
        if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')){
            token = req.headers.authorization.split(" ")[1]
        }

        if(!token){
            return res.status(401).json({errorMessage : "Invalid Authentication"})
        }

        try {
            const decoded = jwt.verify(token, JWT_SECRET);
            console.log(decoded)
            const user = await User.findById(decoded._id || decoded.user._id);
    
            if(!user){
                return res.status(404).json({errorMessage: "No user found"})
            }
    
            req.user = user;
    
            next()
            
        } catch (error) {
            return res.status(500).json({errorMessage: error.message})

            
        }
   
     
        
    }catch (error) {
        return res.status(500).json({errorMessage: "Not authorized"})
        
    }
    
}


export const authAdminMiddleware = async (req, res, next) => {
    try {
        const user = await User.findOne({
            _id: req.user.id
        })
        if(req.user.role === 0){
            return res.status(400).json({errorMessage: "Not Authorized. Admin private route."})
        }
        next()
                
    } catch (error) {
        return res.status(401).json({errorMessage: 'Not Authorized. Admin private route.'})
    }
}


General frontend configuration of API calls:

JavaScript
import axios from 'axios';
import { getCookies } from '../helpers/storage&cookies/storage&cookies';

const config = {
    headers : {
        'Content-Type' : 'application/json',
        'Authorization' : 'Bearer ' + getCookies('token')
    }
}



export const signup = async (data) => {

    const response = await axios.post('http://localhost:5020/api/auth/sign-up', data, config);
    return response;
};

export const accountAuth = async (data) => {
    const response = await axios.post('http://localhost:5020/api/auth/account-authentication/:token', data, config);
    return response;
}


export const signin = async (data) => {

    const response = await axios.post('http://localhost:5020/api/auth/sign-in', data, config);
    return response;
};


export const googleLogin = async (data) => {

    const response = await axios.post('http://localhost:5020/api/auth/google-login', data, config);
    return response;

}


export const forgotPassword = async (data) => {

    const response = await axios.post('http://localhost:5020/api/auth/forgot-password', data, config);
    return response;

}


export const resetPassword = async (data) => {

    const response = await axios.post('http://localhost:5020/api/auth/reset-password/:token', data, config);
    return response;

}




JavaScript
import axios from 'axios';
import { getCookies } from '../helpers/storage&cookies/storage&cookies';


console.log(getCookies('token'))


const config = {
    headers : {
        'Content-Type' : 'application/json',
        'Authorization' : 'Bearer ' + getCookies('token')
    }
}


export const fetchProducts = async () => {

    const response = await axios.get('http://localhost:5020/api/products/get-products');
    return response;
    
};

export const postProduct = async (data) => {
    const response = await axios.post('http://localhost:5020/api/products/create-product', data, config);
    return response;

}


Assuming that sign up and sign in work perfectly, has anyone encountered this problem before?

What I have tried:

I tried to save the token in local storage instead of cookies; to check if it was a token expiration problem; to manage the middleware with a callback instead of with the trycatch; to start the application with the IP address instead of 'localhost'. All to no avail.
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900