Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Killeon Patterson
Killeon Patterson
18,527 Points

login/logout, webtoken

Hello,

  I'm working on a login/logout function on a MERN app. After a user registers their "name", "email", "password", and "kind" (user) with the service, they can login.

When they successful login, they're navigated to the /dashboard page with a greeting of "hi {user.name}"

Before I finished creating the "logout" process that accesses the get route with the res.clearCookie call, the "login" function was working as expected. Since creating the "logout" process that deletes a cookie and navigates the user to back to the "/login" page, the "login" function is not working correctly.

After I start the server. the initial "login" will direct me to the /dashboard page, as expected, but it won't show the greeting. I have to refresh the page. Also, if I logout of that user and sign in as a different user, the previous user's name will show in the greeting. The browser has to be refreshed to show the correct name.

I can't find a bug that would cause this. Can you see any issues with my code?

https://github.com/Kilosince/flight/tree/main

import { useContext } from 'react';
import { UserContext } from '../../context/userContext';
import {useNavigate} from 'react-router-dom'
import axios from 'axios';




export default function Dashboard() {
    const {user} = useContext(UserContext);
  const navigate = useNavigate();
    const logOut = async () => {
      await axios.get("/logout")
      .then(response => {
       navigate('/login')
      })

    }

  return (
    <div>
      <h1>Dashboard</h1>
      {!!user && (<h2>Hi {user.name}!</h2>)}
      <button type='button'  onClick={() => {logOut(user)}}>Log Out</button>
    </div>
  )
}
const User = require('../models/user');
const { hashPassword, comparePassword} = require('../helpers/auth');
const jwt = require('jsonwebtoken');

const test = (req, res) => {
    res.json('test is working')
}

//Register Endpoint
const registerUser = async (req, res) => {
    try {
        const {name, email, password, kind} = req.body;
        // Check is name was entered
        if(!name) {
            return res.json({
                error: 'name is required'
            })
        };
        // Check is password is good
        if(!password || password.length < 6){
            return res.json({
                error: 'Password is required and should be at least 6 characters long'
            })
        };

          if(!kind) {
            return res.json({
                error: 'kind is required'
            })
        };
        // Check email 
        const exist = await User.findOne({email});
        if(exist) {
            return res.json({
                error: 'Email is taken already'
            })
        }

        const hashedPassword = await hashPassword(password);
        // Create user in database
        const user = await User.create({
            name, 
            email, 
            password: hashedPassword, 
            kind
        })

        return res.json(user);
    } catch (error) {
        console.log(error)
    }
};

// Login Endpoint 
const loginUser = async (req, res) => {
 try {
    const {email, password} = req.body;

    // Check is user exists
    const user = await User.findOne({email});
    if(!user) {
        return res.json({
            error: 'No user found'
        })
    }
    // Check if passwords match
    const match = await comparePassword(password, user.password)
    if(match) {
        jwt.sign({email: user.email, id: user._id, name: user.name, kind: user.kind}, process.env.TOKEN_SECRET, {}, (err, token) => {
            if(err) throw err;
            res.cookie('token', token).json(user)

        })
    }
    if(!match) {
        res.json({
            error: "Passwords do not match"
        })
    }
 } catch (error) {
    console.log(error)

 }
}

const getProfile = (req, res) => {
const {token} = req.cookies
if(token) {
    jwt.verify(token, process.env.TOKEN_SECRET, {}, (err, user) => {
        if(err) throw err;
        res.json(user)
    })
} else {
  res.json(null)  
}
}

 //logout the user
const logOut = (req, res) => {

    res.clearCookie('token');
    return res.sendStatus(200);
  }

module.exports = { 
    test,
    registerUser,
    loginUser,
    getProfile,
    logOut
 }

2 Answers

Rohald van Merode
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree seal-36
Rohald van Merode
Treehouse Staff

Hey Killeon Patterson πŸ‘‹

You'll want to have a look at how you've setup your userContext. Currently, the user state is only being set when initializing the userContext, because of this you need that refresh to get the useEffect to update the user state and therefor user.name on the dashboard.

You may want to consider moving your useEffect from the userContext to the Dashboard instead. This way the users profile will be retrieved whenever this component is being rendered instead of just the initial load of the app.

Alternatively you can move a part of your logIn logic to your userContext and update the user right after they sign in.

As for it showing the name of the previous authenticated user this has to do with your state as well. Currently, the cookie is being cleared because you're making a request to /logout but the user state in your context is not being cleared upon signing out. Adding a simple setUser(null) to your logOut function should fix the issue and stop this from happening.

We have a React Authentication course which might be worth checking out. While Laura uses a simplified API and basic auth for the backend, she sets up a solid userContext which might be helpful for this project πŸ™‚

Hope this helps! πŸ˜„

Killeon Patterson
Killeon Patterson
18,527 Points

Hello,

I'm going through the course you suggested and I've run into an error.

I'm attempting to use the sign-in function on this app exercise. When I sign into the app it, displays the <h1>{authUser.name} is Authenticated</h1> and the <p>Your username is {authUser.username}</p>

as is expected. Although, when I refresh the browser I get the error Cannot read properties of null (reading 'name')

On line 6 on Authenticated.js. I've tried changing the useState initial value into an empty object; (unlike the video's code). When I do this the app no longer crashes, but stops showing the {authUser.name} and {authUser.username}.

I've console logged the info on the Authenticated page and it is just receiving that empty object. How can I get this function to work correctly after a refresh?

https://github.com/Kilosince/treehouse-auth/tree/main

Rohald van Merode
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree seal-36
Rohald van Merode
Treehouse Staff

Hey Killeon Patterson πŸ‘‹

That behavior is to be expected at this point of the course. When refreshing the page the authUser state declared in the userContext refreshes back to the initial value of null. In the final stage of the course Laura will explain how to setup cookies to persist the data of the currently signed in user.