Home > OS >  JWT Auth giving error when attempting to login user
JWT Auth giving error when attempting to login user

Time:09-17

Im using JWT with a Rails backend/React frontend. Im attempting to have my user redirected when they log into their account to the /home page of the site. The error I am receiving is

TypeError: Cannot read property 'push' of undefined
Login.<anonymous>
src/components/Login.js:20
 17 |   if(res.user){
 18 |     localStorage.token = res.token
 19 |     this.setState({user:{id:res.user.id, username:res.user.username, token:res.token}, home:res.user.home}, () => {
> 20 |     this.props.history.push('/home')
    | ^  21 |   })
 22 | } else {
 23 |     alert(res.error)

Ive did make a change, to the home component but since reverted back to what it was but still having issues....

my controllers...

class ApplicationController < ActionController::API
  def encode_token(payload)
      JWT.encode(payload, "coffee")
  end 

def auth_header
  # { Authorization: 'Bearer <token>' }
  request.headers['Authorization']
end

def decoded_token
  if auth_header
    token = auth_header.split(' ')[1]
    # header: { 'Authorization': 'Bearer <token>' }
    begin
      JWT.decode(token,'coffee', true, algorithm: 'HS256')
    rescue JWT::DecodeError
      nil
    end
  end
end

def current_user
  if decoded_token
    user_id = decoded_token[0]['user_id']
    @user = User.find_by(id: user_id)
  end
end

def logged_in?
  !!current_user
end

def authorized
  render json: { message: 'Please log in' }, status: :unauthorized unless logged_in?
end
end
----
class UsersController < ApplicationController
    before_action :authorized, only:[:persist]

    def create
        @user = User.create(user_params)
        if @user.valid?
            token = encode_token({user_id: @user.id})   
            render json: @user
        else 
            render json: {error:'failed to create a user'}
        end
    end 

    def login
        @user = User.find_by(username:params[:username])
        if @user && @user.authenticate(params[:password])
            token = encode_token({user_id: @user.id})
            render json: {user:UserSerializer.new(@user), token:token}
        else
            render json: {error:"wrong user or password"}
        end 
        
    end 
    
    def index 
        @user = User.all
        render json: @user
    end
    def persist
        token = encode_token({user_id: @user.id})
        render json: {user:UserSerializer.new(@user), token:token} 
    end 

    private 
    def user_params
        params.permit(:username, :password)
    end 
end

my user_serializer...

class UserSerializer < ActiveModel::Serializer
    attributes :id, :username
    has_many :favorites
    has_many :favorite_coffee_shops, through: :favorites, source: :coffee_shop
    has_many :reviews
    has_many :coffee_shops, through: :reviews
  end

my login component...

import React from "react";
import SignUp from "./SignUp"
import { useHistory } from 'react-router-dom';
import { withRouter } from "react-router";
export default withRouter(Login)

const history = useHistory();

const Login = {
  username: '',
  password: ''
}

   
function handleAuthResponse(res){
     if(res.user){
       localStorage.token = res.token
       this.setState({user:{id:res.user.id, username:res.user.username, token:res.token}, home:res.user.home}, () => {
       this.props.history.push('/home')
     })
   } else {
       alert(res.error)
     }
   }

function handleLogin(e, userInfo) {
    e.preventDefault()
    console.log(e)
 
    fetch('http://localhost:3000/login',{
     method:"POST",
     headers:{
       'Content-Type':'application/json'
     },
     body:JSON.stringify(userInfo)
   })
   .then(res => res.json())
   .then(json => {
     console.log(json)
     if(!json.error){
       handleAuthResponse(json)
     }else {
       alert(json.error)
     }
   })
   .catch(err => console.log(err))
  }
 
  
function handleChange(e) {
  let {name, value} = e.target
    this.setState({
      [name]: value
    })
  }

return (
<div className="login">Welcome Back!
  <form onSubmit={(e) => this.handleLogin(e, this.state)}>
<div>
    <label>UserName</label>
    <br></br>
    <input type="text" name="username" value={this.state.username} onChange={handleChange}/>
    </div>
    <div>
    <label>Password</label>
    <br></br>
    <input type="password" name="password" value={this.state.password} onChange={handleChange}/>
    </div>
    <input type="submit" value="Submit" className="sl-btn" />
  </form>
<div></div>
  <SignUp />
  </div>
  )

and my home component im attempting to push the user to once logged-in

import React from 'react'
import MapContainer  from '../containers/MapContainer'
import NavBar from './NavBar'
import FavoritesContainer  from './FavoritesContainer'

class Home extends React.Component {

    state = {
        reviews: [],
        favorites: [],
    }

    componentDidMount(){
    fetch('http://localhost:3000/reviews')
    .then(res => res.json())
    .then(reviews => this.setState({ reviews }))
    }
              
    addReview = (review) => {
        fetch('http://localhost:3000/reviews',{
            method: "POST",
            headers: {
                "Content-Type" : "application/json",
                Accept: "application/json",
                Authorization: `bearer ${localStorage.token}`
            },
            body: JSON.stringify({ review: review }
            ),
        })
        .then(res => res.json())
        .then(( json => {
            this.setState(prevState => ({
                reviews: [...prevState.reviews, json ]
               }))
        }))
    }
  
    render() {
        return (
               <div className="home">
                   <NavBar />
                   <MapContainer />
                   <FavoritesContainer />
           </div>
        )
}
}

export default Home

I am pretty new dev, please let me know if there is anymore info needed to help diagnose the problem. any advice would be helpful, thank you!!

CodePudding user response:

The problem is about that history.push. First you have to import: import { useHistory } from 'react-router-dom'; into your file, then declare that history like: const history = useHistory() and this error won't be shown anymore.

I used const because mostly I use functional components :D.

CodePudding user response:

The issue is that the this.props.history is undefined. Try to wrap your login component like this.

First import { withRouter } from "react-router"; on top of your Login.js file.

And export it like this export default withRouter(Login).

  • Related