Home > Software engineering >  Unable To Verify Token Type Error React App
Unable To Verify Token Type Error React App

Time:11-26

I am building a React App with authentication using JWTs. I am able to create a new user. I am able to log that user in. But I am unable to update that user's info in the MongoDb it is connected to. The error I am receiving is the following:

'Unable to verify token'

On the front end, the user's info page they are editing looks like the following:

UserInfoPage.js

import { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import { useToken } from '../auth/useToken';
import { useUser } from '../auth/useUser';

export const UserInfoPage = () => {
    const user = useUser();
    const [token,setToken] = useToken();

    const {id, email, info } = user;

    // We'll use the history to navigate the user
    // programmatically later on (we're not using it yet)
    const history = useHistory();

    // These states are bound to the values of the text inputs
    // on the page (see JSX below). 
    const [favoriteFood, setFavoriteFood] = useState(info.favoriteFood || '');
    const [hairColor, setHairColor] = useState(info.hairColor || '');
    const [bio, setBio] = useState(info.bio || '');

    // These state variables control whether or not we show
    // the success and error message sections after making
    // a network request (see JSX below).
    const [showSuccessMessage, setShowSuccessMessage] = useState(false);
    const [showErrorMessage, setShowErrorMessage] = useState(false);

    // This useEffect hook automatically hides the
    // success and error messages after 3 seconds when they're shown.
    // Just a little user interface improvement.
    useEffect(() => {
        if (showSuccessMessage || showErrorMessage) {
            setTimeout(() => {
                setShowSuccessMessage(false);
                setShowErrorMessage(false);
            }, 3000);
        }
    }, [showSuccessMessage, showErrorMessage]);

    const saveChanges = async () => {
        // Send a request to the server to
        // update the user's info with any changes we've
        // made to the text input values
        //alert('Save functionality not implemented yet');
        try{
            const response = await axios.put(`/api/users/${id}`, {
                favoriteFood,
                hairColor,
                bio,
            }, {
                headers: { Authorization: `Bearer ${token}`}
            });

            const { token: newToken } = response.data;
            setToken(newToken);
            setShowSuccessMessage(true);

        }catch(error){
            setShowErrorMessage(true);
            console.log(error);
        }
    }

    const logOut = () => {
        // We'll want to log the user out here
        // and send them to the "login page"
        alert('Log out functionality not implemented yet');
    }
    
    const resetValues = () => {
        // Reset the text input values to
        // their starting values (the data we loaded from the server)
        //alert('Reset functionality not implemented yet');
        
            setFavoriteFood(info.favoriteFood);
            setHairColor(info.hairColor);
            setBio(info.bio);
        
    }
    
    // And here we have the JSX for our component. It's pretty straightforward
    return (
        <div className="content-container">
            <h1>Info for {email}</h1>
            {showSuccessMessage && <div className="success">Successfully saved user data!</div>}
            {showErrorMessage && <div className="fail">Uh oh... something went wrong and we couldn't save changes</div>}
            <label>
                Favorite Food:
                <input
                    onChange={e => setFavoriteFood(e.target.value)}
                    value={favoriteFood} />
            </label>
            <label>
                Hair Color:
                <input
                    onChange={e => setHairColor(e.target.value)}
                    value={hairColor} />
            </label>
            <label>
                Bio:
                <input
                    onChange={e => setBio(e.target.value)}
                    value={bio} />
            </label>
            <hr />
            <button onClick={saveChanges}>Save Changes</button>
            <button onClick={resetValues}>Reset Values</button>
            <button onClick={logOut}>Log Out</button>
        </div>
    );
}

The backend route the request is being sent to:

updateUserInfoRoute.js

import jwt from 'jsonwebtoken';
import { ObjectID } from 'mongodb';
import { getDbConnection } from '../db';

export const updateUserInfoRoute = { 

    path: '/api/users/:userId',
    method: 'put',
    handler: async (req, res) => {

    try{
        const{ authorization } = req.headers;
        const {userId} = req.params;

        const updates = (
            ({
            favoriteFood,
            hairColor,
            bio,
            }) => ({
            favoriteFood,
            hairColor,
            bio,
        }))(req.body);

        if (!authorization){
            return res.status(401).json({message: 'No authorization headers sent'});
        }

        const token = authorization.split('')[1];

        jwt.verify(token, process.env.JWT_SECRET, async (err, decoded ) => {
            if (err) return res.status(401).json({message: 'Unable to verify token'});
            console.log(err);

            const { id } = decoded;

            if (id !== userId) return res.status(403).json({message: 'No authorization to update user data' });

            const db = getDbConnection('react-auth-db');
            const result = await db.collection('users').findOneAndUpdate(
                { _id: ObjectID(id) },
                { $set: { info: updates }},
                { returnOriginal: false},
            );
            const {email, isVerified, info } = result.value;

            jwt.sign({ id, email, isVerified, info }, process.env.JWT_SECRET, {expiresIn: '365d'}, (err, token) =>{
                if(err){
                    return res.status(200).json(err);
                }
                res.status(200).json({ token });
            })
        })
    }catch(error){
        console.log("Mazzo the error is: "   error);
    }
        
    },
}

What is the issue here? Thank you, Ironman

CodePudding user response:

You pass a token from frontend to backend with this format,

Bearer token

But on updateUserInfoRoute.js code, you split the token without space,

const token = authorization.split('')[1];

For this code, the token output will be

e

To resolve that, just add space on the split method,

const token = authorization.split(' ')[1];
  • Related