Maicmi
Maicmi

Reputation: 1104

How can I pass isAuthenticated to let Authenticated Component route to protected route?

I'm implementing protected route by react router v4. I'm trying to pass "isAuthenticated" value from "Login" Component to "Authenticated" Component but I get "false" value.

Maybe I use the wrong way, Could anybody please help to fix this problem?

My code as following:

Login.js provide "isAuthenticated" control

import React, { Component } from 'react';
import { AUTH_TOKEN } from '../constants';
import { USERNAME } from '../constants';
import { graphql, compose } from 'react-apollo';
import { Row, Col, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import gql from 'graphql-tag';

export const Auth = {
    isAuthenticated: false,
  authenticate(cb) {
    this.isAuthenticated = true;
    // setTimeout(cb, 100);
  },
  signout(cb) {
    this.isAuthenticated = false;
    // setTimeout(cb, 100);
  }
};

class Login extends Component {
  state = {
    username: '',
    password: '',
  };

    login = () => {
        Auth.authenticate();
        console.log(Auth.isAuthenticated);
    };

  render() {

    return (
      <Row>
        <Col xs={12} sm={6} md={5} lg={4}>
                    <div className="Login">
                <h4 className="page-header">Login</h4>
                  <form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}>
                    <FormGroup>
                        <ControlLabel>Username</ControlLabel>
                        <br />
                    <input
                            value={this.state.username}
                            onChange={e => this.setState({ username: e.target.value })}
                          type="text"
                          autoFocus
                      />
                    </FormGroup>

                    <FormGroup>
                        <ControlLabel>Password</ControlLabel>
                        <br/>
                        <input
                            value={this.state.password}
                        onChange={e => this.setState({ password: e.target.value })}
                            type="password"
                          />
                        </FormGroup>
                      <div onClick={() => {this._confirm(); this.login(); }}>
                          <Button type="submit" bsStyle="success">Login</Button>
                      </div>
                    </form>
                </div>
            </Col>
        </Row>
  )
};

  _confirm = async () => {
    const { username, password } = this.state;

      const result = await this.props.loginMutation({
        variables: {
          username,
          password,
        },
      });

      const { token } = result;
      this._saveUserData(token, username);

        this.props.history.push(`/`);
  }

  _saveUserData = (token, username) => {
    localStorage.setItem(AUTH_TOKEN, token);
    localStorage.setItem(USERNAME, username);
  }
};

const LOGIN_MUTATION = gql`
  mutation LoginMutation($username: String!, $password: String!) {
    loginMutation(username: $username, password: $password) {
      token
    }
  }
`;

export default compose(
  graphql(LOGIN_MUTATION, { name: 'loginMutation' }),
)(Login);

Authenticated.js need to get "isAuthenticated" value (true) to render the protected route.

import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { Auth } from '../pages/Login';

console.log(Auth.isAuthenticated);

class Authenticated extends Component {
  render() {
        const {
            component: Component, exact, ...rest
        } = this.props;

        return (
        <Route
            {...rest}
            exact={exact}
            render={props => (
            Auth.isAuthenticated ? (
                <Component { ...props} />
            ) : (
                <Redirect to="/login" />
        ))}
        />
        );
    }
}



export default Authenticated;

=== Workaround Solution ===

Authenticated.js -> get the value from localStorage

import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { AUTH_TOKEN, IS_AUTHEN } from '../constants';

class Authenticated extends Component {


  render() {
        const {
            component: Component, exact, ...rest
        } = this.props;
        const isAuthenticated = !!localStorage.getItem(IS_AUTHEN) && !!localStorage.getItem(AUTH_TOKEN);
        console.log(isAuthenticated);

        return (
        <Route
            {...rest}
            exact={exact}
            render={props => (
            isAuthenticated ? (
                <Component { ...props} />
            ) : (
                <Redirect to="/login" />
        ))}
        />
        );
    }
}



export default Authenticated;

Login.js -> Store value by using localStorage.setItem

import React, { Component } from 'react';
import { AUTH_TOKEN, USERNAME, IS_AUTHEN } from '../constants';
import { graphql, compose } from 'react-apollo';
import { Row, Col, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import gql from 'graphql-tag';

class Login extends Component {
  state = {
    username: '',
    password: '',
    authenticated: false,
  };

  render() {

    return (
      <Row>
        <Col xs={12} sm={6} md={5} lg={4}>
                    <div className="Login">
                <h4 className="page-header">Login</h4>
                  <form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}>
                    <FormGroup>
                        <ControlLabel>Username</ControlLabel>
                        <br />
                    <input
                            value={this.state.username}
                            onChange={e => this.setState({ username: e.target.value })}
                          type="text"
                          autoFocus
                      />
                    </FormGroup>

                    <FormGroup>
                        <ControlLabel>Password</ControlLabel>
                        <br/>
                        <input
                            value={this.state.password}
                        onChange={e => this.setState({ password: e.target.value })}
                            type="password"
                          />
                        </FormGroup>
                      <div onClick={() => this._confirm()}>
                          <Button type="submit" bsStyle="success">Login</Button>
                      </div>
                    </form>
                </div>
            </Col>
        </Row>
  )
};

  _confirm = async () => {
    const { username, password } = this.state;

      const result = await this.props.loginMutation({
        variables: {
          username,
          password,
        },
      });

      this.setState({ authenticated: true });
      const { token } = result;
      this._saveUserData(token, username, this.state.authenticated);

        this.props.history.push(`/channel`);
  }

  _saveUserData = (token, username, authenticated) => {
    localStorage.setItem(AUTH_TOKEN, token);
    localStorage.setItem(USERNAME, username);
    localStorage.setItem(IS_AUTHEN, authenticated);
  }
};

const LOGIN_MUTATION = gql`
  mutation LoginMutation($username: String!, $password: String!) {
    loginMutation(username: $username, password: $password) {
      token
    }
  }
`;

export default compose(
  graphql(LOGIN_MUTATION, { name: 'loginMutation' }),
)(Login);

Upvotes: 0

Views: 3046

Answers (1)

Chaitanya Mankala
Chaitanya Mankala

Reputation: 1704

First, On the beginning of the app(index.js), I check for token and set is_auth in my state, like this

<!-- ls is LocalStorageService for get and set from localStorage-->

    if (ls.getUserDetails() && ls.getUserDetails().roles && ls.getUserDetails().roles.length) {
      store.dispatch({ type: SET_USER_ROLE, role: ls.getUserDetails().roles[0] });
      if (ls.getToken()) {
        store.dispatch({ type: AUTHENTICATE_USER, auth: true });
      }
    }
    else {
      store.dispatch({ type: AUTHENTICATE_USER, auth: false });
    }

Then, I made an AuthGuard to validate login status, (by mapping state's auth to this class's props)

AuthGuard.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux'
import { store } from '../stores/configureStore';

export default function (ComposedComponent) {

    // If user not authenticated render out to root

    class AuthGuard extends Component {
        static contextTypes = {
            router: React.PropTypes.object.isRequired
        };

        componentWillMount() {
            if (!this.props.authenticated) {
                //hashHistory.push('#/login');
                store.dispatch(push('/login'));
            }
        }

        componentWillUpdate(nextProps) {
            if (!nextProps.authenticated) {
                //hashHistory.push('#/login');
                store.dispatch(push('/login'));
            }
        }

        render() {
            return <ComposedComponent {...this.props} />;
        }
    }

    const mapStateToProps = (state) => ({
        authenticated: state.auth.auth
    });

    return connect(mapStateToProps)(AuthGuard);
}

And then in my App.js, where I do my routing,

App.js

<!--PROTECTED ROUTES GO AFTER '/app/'-->
        <Route path={`${match.url}app`} component={authGuard(MainApp)} /> 

<!--UNPROTECTED ROUTES GO AFTER '/' LIKE BELOW-->
        <Route exact path="/404" component={Page404} />
        <Route exact path="/403" component={Page403} />
        <Route exact path="/500" component={Page500} />
        <Route exact path="/confirm-email" component={PageConfirmEmail} />
        <Route exact path="/forgot-password" component={PageForgotPassword} />
        <Route exact path="/fullscreen" component={PageFullscreen} />
        <Route exact path="/lock-screen" component={PageLockScreen} />
        <Route exact path="/login" component={PageLogin} />
        <Route exact path="/sign-up" component={PageSignUp} />

Comment below, for any questions :)

Upvotes: 1

Related Questions