spkvn
spkvn

Reputation: 194

Unable to implement auth routes

I'm trying to learn react, and am setting up routes in my application which require you to log in. I'm trying to adapt the example given here

The code I've written should either redirect the user or display the protected route. But when I log in I'm still being redirected.

I believe the issue is in my PrivateRoute class below. I pass it a authenticated property which is set in the parent class, but it doesn't appear to update.

In app.js we declare the authenticator, where we perform async login with our backend.

I pass the checkLoggedIn function to the login component, where we set the parent's authenticated state property to true. I'm console.log()ing the state just to check it's occurring, which it is.

When I then click the Link to /protected route I'm still being redirected.

app.js

// imports ... 

let authenticator = new Authenticator();

class ProtectedComponent extends Component {
    render() {
        return (
            <h1>Protected!</h1>
        );
    }
}

class App extends Component {
    constructor(props){
        super(props);
        this.state = {
            authenticator: authenticator,
            authenticated: authenticator.isLoggedIn(),
        }
    }

    checkLoggedIn() {
        this.setState({authenticated: true});
        console.log(this.state);
    }

    render() {
        let routes, links = null;
        links = <div className="links">
            <Link to="/login">Login</Link>
            <Link to="/protected">Protected</Link>
        </div>;

        routes = <div className="routes">
            <Route
                path="/login"
                render={() =>
                    <Login
                        authenticator={this.state.authenticator}
                        loginCallback={this.checkLoggedIn} />
                }
            />
            <PrivateRoute
                path="/protected"
                component={ProtectedComponent}
                authenticated={this.state.authenticated}
            />
        </div>;

        return (
            <Router className="App">
                {links}
                {routes}
            </Router>
        );
    }
}

export default App;

PrivateRoute.js

// imports .... 

const PrivateRoute = ({ component: Component, authenticated, ...rest }) => (
    <Route {...rest} render={props =>
            authenticated === true
                ? (<Component {...props} />)
                : (<Redirect to={{
                        pathname: "/login",
                        state: { from: props.location }
                    }} />
                )
    }/>
);

export default PrivateRoute;

Login.js

// imports ...

class Login extends Component {
    constructor(props) {
        super(props);
        this.authenticator = props.authenticator;
        this.loginCallback = props.loginCallback;
        this.state = {
            identifier: "",
            password: "",
        }
    }

    updateState = (e, keyName = null) => {
        this.setState({[keyName]: e.target.value})
    }

    attemptLogin = (e) => {
        this.authenticator.loginPromise(this.state.identifier, this.state.password)
            .then(resp => {
                if(resp.data.success === true) {
                    this.authenticator.setToken(resp.data.api_token);
                    this.loginCallback();
                } else {
                    this.authenticator.removeToken()
                }
            })
            .catch(err => {
                console.error(err);
            });
    }

    render(){
        <button onClick={this.attemptLogin}> Log In </button>
    }
}

export default Login;

I'm setting the authenticated state to true in the callback method, but when I go to the protected route (and run it's render method) it appears to be evaluating to false.

If I'm misunderstanding the react props system, let me know. If you'd like to see any more of the code let me know and I'll amend the question.

Upvotes: 0

Views: 133

Answers (1)

Peyman
Peyman

Reputation: 101

You have to create a PrivateRoute HOC component first:

import React from 'react';
import { Route, Redirect } from 'react-router-dom';

export const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route {...rest} render={props => (
        localStorage.getItem('bpm-user')
            ? <Component {...props} />
            : <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
    )} />
)

and wrap your routes that most be protected:

        <Switch>
            <Route path="/login" component={Login} />
            <PrivateRoute path="/new/index" component={NewIndex} />
            <PrivateRoute path="/jobs/index" component={JobsIndex} />
            <PrivateRoute path="/unions/index" component={UnionsIndex} />
            <PrivateRoute exact path="/" component={ListIndex} />
            <PrivateRoute exact path="/charges" component={MunicipalCharges} />
        </Switch>

and use Link

<Link to="/jobs/index">Jobs</Link>

my login reducer

import axios from 'axios';
import * as actionTypes from './AuthActionTypes';

export const login = (user) => {
    return dispatch => {
        // for example => dispatch({type:actionTypes.REQUEST_LOGIN_USER});
        axios({
            method: 'post',
            url: '/api/auth/login',
            data: { 'username': user.username, 'password': user.password },
            headers: { 'Content-Type': 'application/json;charset=utf-8' }
        })
            .then(res => {
                localStorage.setItem('bpm-user', JSON.stringify(res.data));
                dispatch({
                    type: actionTypes.LOGIN_USER,
                    payload: res.data
                })
            })
            .catch(error => {
                 // TODO... for example => dispatch({type:actionTypes.FAILD_LOGIN_USER, payload:error});
            })
    }
}

export const logout = () => {
    localStorage.removeItem('bpm-user');
}

like the example codes that i copied from my own project

Upvotes: 1

Related Questions