0

I have had success implementing the application in its entirety but the only problem I am having deals with authorization when reloading the page. I have set a "token" key in localStorage but it fails to retrieve this key or token (presumably) on reload. I can clearly see it is defined in Chrome's Inspect but when I try to console.log or use that localStorage variable in anyway after reloading it is reads undefined. I can still see the token visible in Chrome's Application Storage and I am not sure how this is possible. The app works properly in development but when deployed to heroku or when express is used to deliver the static file it also stops working and this behavior of returning undefined keeps happening always returning "res.status(403).json("Not Authorized (authorization catch)")" I have gone through many documents in express and jwt along with many SO solutions. It seems most people lose their token but that is not the case here. My server code looks like:

const express= require("express")
const app = express()
const cors = require("cors")
const path = require('path')

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

// ROUTES
// register and login
 app.use("/auth", require("./routes/jwtAuth"))
 app.use("/dashboard", require("./routes/dashboard"))

 app.use("/", express.static(path.join(__dirname, 'client/build')))

 app.get("*", (req, res) => {
     res.sendFile(path.join(__dirname, "client/build/index.html"));
   });

 const PORT = process.env.PORT || 5000

 app.listen(PORT, () => {
     console.log(`Server is running on port ${PORT}`)
 })

And the page that I would like to reload includes:

import React, { Fragment, useState, useEffect } from 'react'
//components
import InputConnection from './connectionlist/InputConnection'
import ListConnections from './connectionlist/ListConnections'
import LogoutBtn from './LogoutBtn'
import ReportingLayout from './reporting/Layout/ReportingLayout'

const Dashboard = ({ setAuth }) => {
const [name, setName] = useState("")
const [allConnections, setAllConnections] = useState([])
const [connectionsChange, setConnectionsChange] = useState(false)
const auth = setAuth

const getName = async () => {
    try {
    
        const response = await fetch("/dashboard/", {
            method:"GET", 
            headers:{ token: localStorage.token }
        }) 

        const parseData = await response.json()
        // console.log(parseData)
    
        if (parseData.admin === 'lead') {
            setName("Lead School Counselor")
            setAllConnections(parseData.results)
        }else{
            setName(parseData[0].user_name)
            setAllConnections(parseData)
        }
    } catch (error) {
    
    }
}

useEffect(() => {
    getName()
    setConnectionsChange(false)
}, [connectionsChange])

if(name === "Lead School Counselor" ){
    return(
        <div>
            <ReportingLayout auth={auth} allConnections={ allConnections } />    
        </div>
       )
}else{
    return(
        <Fragment>
            <div className="container">
                <div className='btn-group '>
                    <LogoutBtn setAuth = {setAuth}/>
            </div>
                <h1 className="d-flex rm-3" > Welcome {name},&nbsp;&nbsp;</h1>
            <InputConnection setConnectionsChange={setConnectionsChange}/>
            <ListConnections allConnections={ allConnections } setConnectionsChange= 
{setConnectionsChange}/> 
        </div>     
    </Fragment>
)
}
}

export default Dashboard;   

This is where the code fails. It is a middleware that deals with the authorization and prinst the error message from step 1:

const jwt = require("jsonwebtoken")
require("dotenv").config()

module.exports = async (req, res, next) => {
try {

// step 1 destructure
const jwtToken = req.header("token")

if(!jwtToken){
    return res.status(403).json("Not Authorized (authorization not jwt Token)")
}
// step 2 check if the token is valid 
const payload = jwt.verify(jwtToken, process.env.jwtSecret)
// step 3 gives access as req.user
req.user = payload.user
next()

} catch (err) {
    console.error(err.message)
    return res.status(403).json("Not Authorized (authorization catch)")
}
}

App.js:

import React, { Fragment, useState, useEffect } from 'react';
import './App.css';
import {BrowserRouter as Router, Switch, Route, Redirect} from 'react- 
router-dom'

// components 
import Dashboard from './components/dashboard/Dashboard'
 import Login from './components/Login'
import Register from './components/Register'
import Landing from './components/Landing'

//toastify 
import "react-toastify/dist/ReactToastify.css";
import { toast } from "react-toastify";

toast.configure()  

function App() {

  const [isAuthenticated, setIsAuthenticated] = useState(false)

  const setAuth = (boolean) => {
    setIsAuthenticated(boolean)
  }

  async function isAuth(){
    try {
  
  const response = await fetch("/auth/is-verified", {
    method:"GET", 
    headers:{token: localStorage.getItem("token") }
  app.s    })
  const parseRes = await response.json()
  console.log(`this message is ${parseRes}`)
  parseRes === true ? setIsAuthenticated(true): setIsAuthenticated(false)

} catch (err) {
    console.error(err.message)
    }
   }

useEffect(() => {
  isAuth()
})

return (
  <Fragment>
    <Router>
      <div>
         <Switch>
           <Route exact path="/landing" render={props => !isAuthenticated 
? <Landing {...props} /> : <Redirect to='/dashboard'/>} />
        <Route exact path="/register" render={props => !isAuthenticated ? 
<Register {...props} setAuth ={setAuth} />  : <Redirect to='/login'/>} />
        <Route exact path="/login" render={props => !isAuthenticated ? 
<Login {...props} setAuth ={setAuth} auth={isAuthenticated}/> : <Redirect 
to='/dashboard'/>} />
        <Route exact path="/dashboard" render={props => isAuthenticated ? 
<Dashboard {...props} setAuth ={setAuth} /> : <Redirect to='/login'/>} />
      </Switch>
    </div>
  </Router>
</Fragment>

  );
}

export default App;

here is the dashboard.js db and connections:

const router = require("express").Router()
const pool = require("../db")
const authorization = require("../middleware/authorization")

// all connections and name
router.get("/", authorization, async (req, res) => {
try {
  res.json(req.user.name)
    if(req.user.name === 'lead'){
      const lead = await pool.query("SELECT * FROM connections LEFT JOIN 
   users ON users.user_id = connections.user_id")                                                                                                      
   ... A bunch of sql queries here that work fine...
      res.json({
        admin: req.user.name,
        results: lead.rows,
        // aggregated queries
        studentsEngaged: studentsEngaged.rows,
        gender:gender.rows,
        distinctStudents: distinctStudents.rows,
        amountSep: amountSep.rows,
        amountOct:amountOct.rows,
        amountNov: amountNov.rows,
        amountDec: amountDec.rows,

        studentSessions:studentSessions.rows,
        homeVisits: homeVisits.rows,
        outsideAgencies: outsideAgencies.rows,
        cpReferrals: cpReferrals.rows,
        amountReferrals:amountReferrals.rows,
        amountDischarges: amountDischarges.rows,
        classroomPresentations: classroomPresentations.rows,
        groupSessions: groupSessions.rows,
        checkins: checkins.rows,
        crisisInterventions: crisisInterventions.rows,
        parentContacts: parentContacts.rows,
        meetings : meetings.rows
      })
    }else{
      const user = await pool.query("SELECT u.user_name, c.connection_id, 
c.contact_type, c.contact_method, c.provision, c.connection_date, 
c.student_id, 
c.purpose, c.gender, c.yearGroup, c.school, c.referral_discharge, 
c.cp_referral 
FROM users AS u LEFT JOIN connections AS c ON u.user_id = c.user_id WHERE 
u.user_id= $1", [req.user.id])                                                                                                      
      res.json(user.rows)
    }
} catch (err) {
    console.error(err.message)
    res.status(500).json("Server Error (dashboard catch)")
}
})

// create connection
router.post("/connections", authorization, async (req, res) => {
try {
  // console.log(req.body);
  const { student_id, contact_type, yearGroup, school, contact_method, 
gender, purpose, provision, connection_date, referral_discharge, cp_referral} 
= 
req.body;
  const newConnection = await pool.query(
    "INSERT INTO connections (user_id, student_id, user_name, contact_type, 
yearGroup, school, contact_method, gender, purpose, provision, 
connection_date, 
referral_discharge, cp_referral) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 
$10, $11, $12, $13) RETURNING *",
    [req.user.id, student_id, req.user.name, contact_type, yearGroup, school, 
contact_method, gender, purpose, provision, connection_date, 
referral_discharge, 
cp_referral]
  );
  res.json(newConnection.rows[0]);
  // console.log(newConnection.rows[0])
} catch (err) {
  console.error(err.message)
 }
 });

// update connection 
router.put("/connections/:id", authorization, async (req, res) => {
try {
  const { id } = req.params;
  const { student_id, contact_type, yearGroup, school, contact_method, 
gender, purpose, provision, connection_date, referral_discharge, cp_referral 
} = 
req.body;
  const updateConneciton = await pool.query(
    "UPDATE connections SET student_id=$1, contact_type=$2, yearGroup=$3, 
 school=$4, contact_method=$5, gender=$6, purpose=$7, provision=$8, 
connection_date=$9, referral_discharge=$10, cp_referral=$11 WHERE 
connection_id = 
$12 AND user_id = $13 RETURNING *",
    [student_id, contact_type, yearGroup, school, contact_method, gender, 
purpose, provision, connection_date, referral_discharge, cp_referral, id, 
req.user.id]
  );     

  if (updateConneciton.rows.length === 0) {
    return res.json("This connection is not yours");
  }

  res.json("Connection was updated");
} catch (err) {
  console.error(err.message);
}
 });

// delete connection
router.delete("/connections/:id", authorization, async (req, res) => {
try {
  const { id } = req.params;
  const deleteConnection = await pool.query(
    "DELETE FROM connections WHERE connection_id = $1 AND user_id = $2 
RETURNING *",
    [id, req.user.id]
  );

  if (deleteConnection.rows.length === 0) {
    return res.json("This connection is not yours");
  }

  res.json("Connection was deleted");
} catch (err) {
  console.error(err.message);
}
 }) ;

At input is appended to the headers:

import React, { Fragment, useState } from "react";
import { toast } from 'react-toastify'

const InputTodo = ({ setConnectionsChange }) => {
const [contact_type, setContactType] = useState("");
const [contact_method, setContactMethod] = useState("");
const [provision, setProvision] = useState("");
const [connection_date, setDate] = useState("");
const [student_id, setStudentID] = useState("");
const [purpose, setPurpose] = useState("");
const [gender, setGender] = useState("");
const [yearGroup, setYearGroup] = useState("");
const [school, setSchool] = useState("");
const [referral_discharge, setReferralDischarge] = useState("");
const [cp_referral, setCPReferral] = useState("");

const onSubmitForm = async e => {
 e.preventDefault();
 try {
   const myHeaders = new Headers();

  myHeaders.append("Content-Type", "application/json");
  myHeaders.append("token", localStorage.token);

  const body = { 
                  contact_type, 
                  contact_method, 
                  provision,
                  connection_date,
                  student_id,
                  purpose,
                  gender,
                  yearGroup,
                  school, 
                  referral_discharge, 
                  cp_referral
                };
  const response = await fetch("/dashboard/connections", {
    method: "POST",
    headers: myHeaders,
    body: JSON.stringify(body)
  });

  const parseResponse = await response.json();

  console.count(parseResponse);

  setConnectionsChange(true);
  setContactType("")
  setContactMethod("")
  setProvision("")
  setDate("")
  setStudentID("")
  setPurpose("")
  setGender("")
  setYearGroup("")
  setSchool("")
  setReferralDischarge("")
  setCPReferral("")

  toast.success('Contact has been added', {
    position: "top-center",
    autoClose: 3000,
    hideProgressBar: false,
    closeOnClick: true,
    pauseOnHover: true, 
    draggable: true,
    });

} catch (error) {
  console.error(error.message);
 }
 };
  return (
    <Fragment> A form 
    </Fragment>
    )

Authorization check on back end:

const router = require("express").Router()
const pool = require("../db")
const bcrypt = require("bcrypt")
const jwtGenerator = require("../utils/jwtGenerator")
const validInfo = require("../middleware/validInfo")
const authorization = require("../middleware/authorization")
// registering
router.post("/register", validInfo, async(req, res) =>{
try {
    
    // step1 destructure
        const { name, email, password } = req.body
        
    // step2 check if the user exists 
        const user = await pool.query("SELECT * FROM users WHERE 
user_email=$1", [email])
        if(user.rows.length >0){
            return res.status(401).json("User already exists; email is 
already registered with the database")
        }

    // step3 bcrypt the user password for db 
        const saltRound = 10;
        const salt = await bcrypt.genSalt(saltRound)

        const bcryptPassword = await bcrypt.hash(password, salt)

    // step4 insert the info into the db 
        const newUser = await pool.query("INSERT INTO users (user_name, 
user_email, user_password) VALUES ($1, $2, $3) RETURNING *", [name, email, 
bcryptPassword])
   
    // step5 generate a jwt token 
        const token = jwtGenerator(newUser.rows[0].user_id, 
newUser.rows[0].user_name)
        res.json({ token })

} catch (err) {
    console.error(err.message)
    res.status(500).json("Server Error (register)")
}
})

 // login and logout 
router.post("/login", validInfo, async (req, res) => {
try {
    // step1 deconstruct req.body 
        const { email, password } = req.body
    // step 2 check if user doesnt exist and if not throw and error 
        const user = await pool.query("SELECT * FROM users WHERE 
user_email=$1", [email])
        if(user.rows.length === 0){
            return res.status(401).json("User email is incorrect or does not 
exist.")
        }
    // step 3 check if incoming pword is the same as db password 
        const validPassword = await bcrypt.compare(password, 
user.rows[0].user_password)
        if(!validPassword){
            return res.status(401).json("Password is incorrect.")
        }
    // step4 give them a jwt token 
    const token = jwtGenerator(user.rows[0].user_id, user.rows[0].user_name)
    res.json({ token })
} catch (err) {
        console.log(err.Message)
        res.statusMessage(500).json("Server Error (login)")
    }
})

router.get("/is-verified", authorization, (req, res) => {
   try {
        res.json(true)
    } catch (error) {
        console.log(err.Message)
        res.statusMessage(500).json("Server Error (is-verified)")
     }
})

module.exports = router;

Instead of reloading, I will type in "/login" into the url and it will then redirect me (using the routes set up) to dashboard no problem. Could I just direct the user to log in on reloading and if so what is an optimal way to implement it?

John Ketterer
  • 137
  • 1
  • 1
  • 9
  • should it be localStorage.getItem('tokenName')? how are you getting item from local storage? – Nonik Oct 01 '20 at 00:17
  • It is passed through the headers. It is working correctly as is when it is called as localStorage.token – John Ketterer Oct 01 '20 at 00:20
  • There is this I found but didn't make a clear statement, I did try it though: https://www.oreilly.com/library/view/learn-ecmascript-/9781788620062/11b7d708-f6c9-41d2-b066-197e79acbf6e.xhtml#:~:text=getItem('key%2Ddoes%2Dnot,such%20as%20getItem%20and%20setItem%20. – John Ketterer Oct 01 '20 at 00:22
  • Where all your database logic and connection? – ousecTic Oct 01 '20 at 03:27
  • How do you send the token to your client? How do you attach it with a request to your server? – orimdominic Oct 01 '20 at 04:35
  • added some code; I use state on the client side based on the presence of a token in the headers. The token in the headers remains after reloading but is undefined in the program. I am not sure how localStorage gets left out and ignored during the reload. – John Ketterer Oct 01 '20 at 05:06
  • Similar issue here: https://stackoverflow.com/questions/58751020/how-do-i-keep-state-persistant-using-local-storage-react-hooks-and-context-pro – John Ketterer Oct 01 '20 at 05:38

0 Answers0