POST http://localhost:3000/api/v1/stories 401 (Unauthorized)

  authentication, javascript, post, reactjs, redux

Hi I am new to redux and authentication. I am creating a react/redux app where a user can login and be able to add a story by submitting a form. I am able to login but when I get to the story creation page, I click submit and I get a POST http://localhost:3000/api/v1/stories 401 (Unauthorized) error.
I am logging in using an API that gives a token on login. I then save the username and token to sessionstorage. But how would I fix this error?

App.js

import './App.scss';
import Login from './components/Login';
import { Router, Switch, Route, NavLink } from 'react-router-dom';
import PrivateRoute from './utils/PrivateRoute';
import CreateStory from './components/CreateStory';
import history from './utils/history';

function App() {


  return (
    <div className="App">

      <Router history={history}>
        <Switch>
          <Route exact path="/" component={Login} />
          <PrivateRoute path="/user" component={CreateStory}/>

        </Switch>
      </Router>
    </div>
  );
}

export default App;

PrivateRoute.js

import { useSelector } from 'react-redux'
 
// handle the private routes
function PrivateRoute({ component: Component, ...rest }) {

  const getToken = useSelector((state)=> state.loginReducer.token)
  console.log(getToken)
  return (
    <Route
      {...rest}
      render={(props) => getToken ? <Component {...props} /> : <Redirect to={{ pathname: '/', state: { from: props.location } }} />}
    />
  )
}
 
export default PrivateRoute;

CreateStory.js

import React, { useState } from 'react'
import { createStory } from '../redux/actions'
import { useDispatch } from "react-redux";

const CreateStory = () => {

    const [summary, setSummary] = useState("");
    const [description, setDescription] = useState("");
    const [type, setType] = useState("");
    const [complexity, setcomplexity] = useState("");

    const usedispatch = useDispatch();
    const userCreateStory = (summary, description, type, complexity) => usedispatch(createStory({
                                                                                    'summary': summary,
                                                                                    'description': description,
                                                                                    'type': type,
                                                                                    'complexity': complexity 
                                                                                }));

    const handleSummaryChange = e => {
        setSummary(e.target.value)
    }  
    
    const handleDescriptionChange = e => {
        setDescription(e.target.value)
    }

    const handleTypeChange = e => {
        setType(e.target.value)
    }

    const handleComplexityChange = e => {
        setcomplexity(e.target.value)
    }

    const handleSubmit = e => {
        e.preventDefault();
        userCreateStory('a','b','c','d')
      //  setTimeout(()=> history.push("/user"), 1000 );
    }

    return (
        <div>
            <form className='create-story-form'>
                <label for="summary">Summary:</label>
                <input name="summary" type='text' onChange={handleSummaryChange}/>
                <label for="desc">Description:</label>
                <textarea name="desc" type='text' onChange={handleDescriptionChange}/>
                <label for="type">Type:</label>
                <select name="type">
                    <option value="enhancement">Enchancement</option>
                    <option value="bugfix">Bugfix</option>
                    <option value="development">Development</option>
                    <option value="qa">QA</option>
                </select>
                <label for="complexity">Complexity:</label>
                <select name="complexity">
                    <option value="Low">Low</option>
                    <option value="Mid">Mid</option>
                    <option value="High">High</option>
                </select>
                <label for="time">Estimated time for completion:</label>
                <input name="time" type='text' />
                <label for="cost">Cost:</label>
                <input name="cost" type='number' />
                <button onClick={handleSubmit}>Submit</button>
            </form>
        </div>
    )
}

export default CreateStory;

Login.js

import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { login, roleChange } from '../redux/actions' //OUR ACTIONS
import { useSelector } from 'react-redux'
import history from '../utils/history';
import { withRouter } from 'react-router-dom';

const Login = () => {

    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");

    const usedispatch = useDispatch();
    const userLogin = (email, password) => usedispatch(login({'email': email, 'password': password }));
    const switchToAdmin = () => usedispatch(roleChange('admin'));
    const switchToUser = () => usedispatch(roleChange('user'));
    const currentRole = useSelector((state)=> state.loginReducer.role)

    const handleRoleChange = e => {
        e.preventDefault();
        if(currentRole === 'user')
            switchToAdmin();
        else if(currentRole === 'admin' )
            switchToUser()
    }
    
    const handleEmailChange = e => {
        setEmail(e.target.value)
    }

    const handlePasswordChange = e => {
        setPassword(e.target.value)
    }

    const handleSubmit = e => {
        e.preventDefault();
        userLogin(email, password)
        setTimeout(()=> history.push("/user"), 1000 );
    }

    const disabled = () => {
        return email === "" || password === ""
    }

   

    return (
        <div>
            <form className='login-form'>
                <input type='email' name='email' placeholder='Email' onChange={handleEmailChange}/>
                <input type='password' name='password' placeholder='Password' onChange={handlePasswordChange}/>
                <button type='submit' disabled={disabled()} onClick={handleSubmit}>Login</button>
            </form>
            <button onClick={handleRoleChange}>Switch to {currentRole === 'user' ? 'admin' : 'user'}</button>
        </div>
    )
}

export default withRouter(Login);

actionTypes.js

export const SET_LOGIN_STATE = "SET_LOGIN_STATE"
export const SET_ROLE_STATE = "SET_ROLE_STATE"
export const CREATE_STORY = "CREATE_STORY"

initialState.js:

import { getToken } from '../utils/Common'

export const initialState = {
    isLoggedIn: false,
    userId: '',
    role: 'user',
    token: getToken,
    data: '',
  };

reducers.js

import { initialState } from './initialState';
import * as t from './actionTypes';

export const loginReducer = (state = initialState, action) => {
  switch (action.type) {
    case t.SET_ROLE_STATE:
      return {
        ...state,
        role: action.payload,
      };
    case t.SET_LOGIN_STATE:
      return {
        ...state,
        ...action.payload, // this is what we expect to get back from API call and login page input
        isLoggedIn: true, // we set this as true on login
      };
    default:
      return state;
  } 
};

export const storyReducer = (state = initialState, action) => {
  switch (action.type) {
    case t.CREATE_STORY:
      return {
        ...state,
        role: action.payload,
      };
    default:
      return state;
  } 
}

actions.js:

import * as t from './actionTypes';
import { setUserSession } from '../utils/Common';

// this is what our action should look like which dispatches the "payload" to reducer
const setLoginState = (loginData) => {
  return {
    type: t.SET_LOGIN_STATE,
    payload: loginData, //{ ...json, userId: email }
  };
};

const setStoryState = (storyData) => {
    return {
      type: t.CREATE_STORY,
      payload: storyData,
    };
  };

export const login = (loginInput) => { //our login action
    const { email, password } = loginInput;
    return (dispatch) => {  // don't forget to use dispatch here!
      return fetch('http://localhost:3000/api/v1/signin', {
        method: 'POST',
        headers: {  
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(loginInput),
      })
        .then((response) => response.json()) //json will be the response body
        .then((json) => {
        // if (json.msg === 'success') { // response success checking logic could differ
           // console.log(json)
            dispatch(setLoginState({ ...json, userId: email })); // our action is called here with object as parameter, this is our payload
            //we appended json object to our state
            //   } else {
        //     alert('Login Failed', 'Email or Password is incorrect');
        //  }
            setUserSession(json.token, json.lastName)
        })
        .catch((err) => {
          alert('Login Failed', 'Some error occured, please retry');
          console.log(err);
        });
    };
};

export const roleChange = role => {
    return {
        type: t.SET_ROLE_STATE,
        payload: role
      };
}

/**
 * story input:
{
  "summary": "string",
  "description": "string",
  "type": "string",
  "complexity": "string"
}
 */

export const createStory = storyInput => {
    const { summary, description, type, complexity } = storyInput;
    return (dispatch) => {  // don't forget to use dispatch here!
      return fetch('http://localhost:3000/api/v1/stories', {
        method: 'POST',
        headers: {  
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(storyInput),
      })
        .then((response) => response.json()) //json will be the response body
        .then((json) => {
        // if (json.msg === 'success') { // response success checking logic could differ
            console.log(json)
            // dispatch(setStoryState({  // our action is called here with object as parameter, this is our payload
            //     summary: summary,
            //     description: description,
            //     type: type,
            //     complexity: complexity
            // })); // our action is called here
        //   } else {
        //     alert('Login Failed', 'Email or Password is incorrect');
        //  }
        })
        .catch((err) => {
          alert('Some error occured, please retry');
          console.log(err);
        });
    };
}

Common.js

// return the user data from the session storage
export const getUser = () => {
    const userStr = sessionStorage.getItem('user');
    if (userStr) return JSON.parse(userStr);
    else return null;
}
   
// return the token from the session storage
export const getToken = () => {
    return sessionStorage.getItem('token') || null;
}
   
// remove the token and user from the session storage
export const removeUserSession = () => {
    sessionStorage.removeItem('token');
    sessionStorage.removeItem('user');
}
   
// set the token and user from the session storage
export const setUserSession = (token, user) => {
    sessionStorage.setItem('token', token);
    sessionStorage.setItem('user', JSON.stringify(user));
}

Source: Ask Javascript Questions

LEAVE A COMMENT