Authentication Diagram




Register



Constant

  • File: app\redux\constants\authConstants.js
  • Define constant:
                              
    export const REGISTER_WITH_EMAIL_REQUEST = 'REGISTER_WITH_EMAIL_REQUEST';
    export const REGISTER_WITH_EMAIL_SUCCESS = 'REGISTER_WITH_EMAIL_SUCCESS';
    export const REGISTER_WITH_EMAIL_FAILURE = 'REGISTER_WITH_EMAIL_FAILURE';
                              
                            

Actions

  • File app\redux\actions\authActions.js
  • Put each constant in actions
                              
    export const registerWithEmail = (name, email, password) => ({
      type: types.REGISTER_WITH_EMAIL_REQUEST,
      name,
      email,
      password
    });
    
    export const registerWithEmailSuccess = credential => ({
      type: types.REGISTER_WITH_EMAIL_SUCCESS,
      credential
    });
    
    export const registerWithEmailFailure = error => ({
      type: types.REGISTER_WITH_EMAIL_FAILURE,
      error
    });
                              
                            

Saga

  • File app\redux\modules\authSagas.js
  • Call register action in saga's function
                              
    function* registerWithEmailSaga(payload) {
      try {
        yield call(firebaseAuth.createUserWithEmailAndPassword, payload.email, payload.password);
        const dataWithName = yield call(firebaseAuth.updateProfile, {
          displayName: payload.name,
        });
        yield put(registerWithEmailSuccess(dataWithName));
        // Redirect to dashboard
        yield history.push('/app');
      } catch (error) {
        yield put(registerWithEmailFailure(error));
      }
    }
    
    function* createUserSaga({ credential }) {
      try {
        yield call(firebaseDb.create, 'user', {
          email: credential.email,
          displayName: credential.displayName,
          creationTime: credential.metadata.creationTime,
        });
        yield put(createUserSuccess(credential));
      } catch (error) {
        yield put(createUserFailure(error));
      }
    }
    
    function* loginRootSaga() {
      yield fork(syncUserSaga);
      yield all([
        takeEvery(REGISTER_WITH_EMAIL_REQUEST, registerWithEmailSaga),
        takeEvery(REGISTER_WITH_EMAIL_SUCCESS, createUserSaga),
      ]);
    }
                              
                            

Reducers

  • File app\redux\modules\authReducer.js
  • Put all payload once action with constant REGISTER_WITH_EMAIL_REQUEST and REGISTER_WITH_EMAIL_FAILURE in reducer state. So the font-end part (React components) can get the state to modify ui or contents.
                              
        case REGISTER_WITH_EMAIL_REQUEST:
          return {
            ...state,
            loading: true,
            message: null
          };
        case REGISTER_WITH_EMAIL_FAILURE:
          return {
            ...state,
            loading: false,
            message: action.error.message
          };
                              
                            

Container

  • File: app\containers\Pages\Users\Register.js
  • Call the Register function in register container
                              
    import { registerWithEmail } from 'enl-redux/actions/authActions';
    Register.propTypes = {
      handleRegisterWithEmail: PropTypes.func.isRequired,
    };
                              
                            
  • Send register form value
                              
    submitForm(values) {
      setTimeout(() => {
        this.setState({ valueForm: values });
        console.log(`You submitted:\n\n${this.state.valueForm.get('email')}`); // eslint-disable-line
        this.props.handleRegisterWithEmail(this.state.valueForm.get('name'), this.state.valueForm.get('email'), this.state.valueForm.get('password')); // eslint-disable-line
      }, 500); // simulate server latency
    }
                              
                            

Component

  • File: app\components\Forms\RegisterForm.js
  • Collect register form value
                            
     <form onSubmit={handleSubmit}>
        <div>
          <FormControl className={classes.formControl}>
            <Field
              name="name"
              component={TextField}
              placeholder={intl.formatMessage(messages.loginFieldName)}
              label={intl.formatMessage(messages.loginFieldName)}
              required
              className={classes.field}
            />
          </FormControl>
        </div>
    ...
     </form>                          
                              
                            


Login



Constants

  • File: app\redux\constants\authConstants.js
  • Define constant:
                            
    // Use For Social Media Login                        
    export const LOGIN_REQUEST = 'LOGIN_REQUEST';
    export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
    export const LOGIN_FAILURE = 'LOGIN_FAILURE';
    
    // Use For Email Login  
    export const LOGIN_WITH_EMAIL_REQUEST = 'LOGIN_WITH_EMAIL_REQUEST';
    export const LOGIN_WITH_EMAIL_SUCCESS = 'LOGIN_WITH_EMAIL_SUCCESS';
    export const LOGIN_WITH_EMAIL_FAILURE = 'LOGIN_WITH_EMAIL_FAILURE';
                            
                          

Actions

  • File app\redux\actions\authActions.js
  • Put each constant in actions
                              
    // Social media login                          
    export const login = authProvider => ({
      type: types.LOGIN_REQUEST,
      payload: { authProvider }
    });
    
    export const loginSuccess = credential => ({
      type: types.LOGIN_SUCCESS,
      credential
    });
    
    export const loginFailure = error => ({
      type: types.LOGIN_FAILURE,
      error
    });
    
    export const signInWithGithub = () => login(
      new firebase.auth.GithubAuthProvider()
    );
    
    export const signInWithGoogle = () => login(
      new firebase.auth.GoogleAuthProvider()
    );
    
    export const signInWithTwitter = () => login(
      new firebase.auth.TwitterAuthProvider()
    );
    
    // Email login  
    export const loginWithEmail = (email, password) => ({
      type: types.LOGIN_WITH_EMAIL_REQUEST,
      email,
      password
    });
    
    export const loginWithEmailSuccess = credential => ({
      type: types.LOGIN_WITH_EMAIL_SUCCESS,
      credential
    });
    
    export const loginWithEmailFailure = error => ({
      type: types.LOGIN_WITH_EMAIL_FAILURE,
      error
    });
                            
                          

Saga

  • File: app\redux\modules\authSagas.js
  • Call social login action in saga's function
                            
    function* loginSaga(provider) {
      try {
        const data = yield call(firebaseAuth.signInWithPopup, provider.payload.authProvider);
        yield put(loginSuccess(data));
        if (getUrlVars().next) {
          // Redirect to next route
          yield history.push(getUrlVars().next);
        } else {
          // Redirect to dashboard if no next parameter
          yield history.push('/app');
        }
      } catch (error) {
        yield put(loginFailure(error));
      }
    }
                            
                          
  • Call email login action in saga's function
                            
    function* loginWithEmailSaga(payload) {
      try {
        const data = yield call(firebaseAuth.signInWithEmailAndPassword, payload.email, payload.password);
        yield put(loginWithEmailSuccess(data));
        if (getUrlVars().next) {
          // Redirect to next route
          yield history.push('/app/' + getUrlVars().next);
        } else {
          // Redirect to dashboard if no next parameter
          yield history.push('/app');
        }
      } catch (error) {
        yield put(loginWithEmailFailure(error));
      }
    }
                            
                          
  • Combine them in single function
                            
    function* loginRootSaga() {
      yield fork(syncUserSaga);
      yield all([
        takeEvery(LOGIN_REQUEST, loginSaga),
        takeEvery(LOGIN_WITH_EMAIL_REQUEST, loginWithEmailSaga),
      ]);
    }
                            
                          

Reducer

  • File: app\redux\modules\authReducer.js
  • Put all payload once action with constant LOGIN_WITH_EMAIL_REQUEST, LOGIN_REQUEST LOGIN_FAILURE, LOGIN_WITH_EMAIL_FAILUREin reducer state. So the font-end part (React components) can get the state to modify ui or contents.
  •                         
    case LOGIN_REQUEST:
    case LOGIN_WITH_EMAIL_REQUEST:
      return {
        ...state,
        loading: true,
        message: null
      };
      
    case LOGIN_FAILURE:
    case LOGIN_WITH_EMAIL_FAILURE:
      return {
        ...state,
        loading: false,
        message: action.error.message
      };
                            
                          

Containers

  • File: app\containers\Pages\Users\Login.js
  • Call the Login function in login container
                            
    import { loginWithEmail } from 'enl-redux/actions/authActions';
    const mapDispatchToProps = dispatch => ({
      handleLoginWithEmail: bindActionCreators(loginWithEmail, dispatch)
    });
                            
                          
  • Send login form value
                            
    submitForm(values) {
      const { valueForm } = this.state;
      setTimeout(() => {
        this.setState({ valueForm: values });
        console.log(`You submitted:\n\n${valueForm}`);
        this.props.handleLoginWithEmail(this.state.valueForm.get('email'), this.state.valueForm.get('password')); // eslint-disable-line
      }, 500); // simulate server latency
    }
                            
                          

Component Email Login

  • File: app\components\Forms\LoginForm.js
  • Collect login form value
                            
    <form onSubmit={handleSubmit}>
      <div>
        <FormControl className={classes.formControl}>
          <Field
            name="email"
            component={TextField}
            placeholder={intl.formatMessage(messages.loginFieldEmail)}
            label={intl.formatMessage(messages.loginFieldEmail)}
            required
            validate={[required, email]}
            className={classes.field}
          />
        </FormControl>
      </div>
      ...
    </form>  
                            
                          

Component Social Login

  • File: app\components\Forms\LoginForm.js
  • Import social login function
                            
    import {
      signInWithGithub,
      signInWithGoogle,
      signInWithTwitter,
    } from 'enl-redux/actions/authActions';
                            
                          
  • Dispatch social login function
                            
    const mapDispatchToProps = {
      signInWithGithubFn: signInWithGithub,
      signInWithGoogleFn: signInWithGoogle,
      signInWithTwitterFn: signInWithTwitter,
    };
                            
                          
  • Call the function when button onClick
                            
    <section className={classes.socmedSideLogin}>
      <Button
        variant="contained"
        className={classes.redBtn}
        type="button"
        size="large"
        onClick={signInWithGoogleFn}
      >
        <Ionicon icon="logo-google" />
        Google
      </Button>
      <Button
        variant="contained"
        className={classes.cyanBtn}
        type="button"
        size="large"
        onClick={signInWithTwitterFn}
      >
        <Ionicon icon="logo-twitter" />
        Twitter
      </Button>
      <Button
        variant="contained"
        className={classes.greyBtn}
        type="button"
        size="large"
        onClick={signInWithGithubFn}
      >
        <Ionicon icon="logo-github" />
        Github
      </Button>
    </section>
                            
                          


Forgot Password



Constant

  • File: app\redux\constants\authConstants.js
  • Define constant:
                            
    export const PASSWORD_FORGET_REQUEST = 'PASSWORD_FORGET_REQUEST';
    export const PASSWORD_FORGET_SUCCESS = 'PASSWORD_FORGET_SUCCESS';
    export const PASSWORD_FORGET_FAILURE = 'PASSWORD_FORGET_FAILURE';
                            
                          

Actions

  • File: app\redux\actions\authActions.js
  • Put each constant in actions
                            
    export const passwordForget = (email) => ({
      type: types.PASSWORD_FORGET_REQUEST,
      email
    });
    
    export const passwordForgetSuccess = credential => ({ // eslint-disable-line
      type: types.PASSWORD_FORGET_SUCCESS
    });
    
    export const passwordForgetFailure = error => ({
      type: types.PASSWORD_FORGET_FAILURE,
      error
    });
                            
                          

Saga

  • File: app\redux\modules\authSagas.js
  • Call social forgot password action in saga's function
                            
    function* passwordForgetSaga({ email }) {
      try {
        yield call(firebaseAuth.sendPasswordResetEmail, email);
        yield put(passwordForgetSuccess());
      } catch (error) {
        yield put(passwordForgetFailure(error));
      }
    }
    
    function* loginRootSaga() {
      yield all([
        takeEvery(PASSWORD_FORGET_REQUEST, passwordForgetSaga)
      ]);
    }
                            
                          

Reducer

  • File: app\redux\modules\authReducer.js
  • Put all payload once action with constant PASSWORD_FORGET_FAILURE:, PASSWORD_FORGET_SUCCESS reducer state. So the font-end part (React components) can get the state to modify ui or contents.
                            
    case PASSWORD_FORGET_FAILURE:
      return {
        ...state,
        loading: false,
        message: action.error.message
      };
    
    case PASSWORD_FORGET_SUCCESS:
      return {
        ...state,
        message: 'LINK.PASSWORD_RESET.SENT'
      };
                            
                          

Containers

  • File: app\containers\Pages\Users\ResetPassword.js
  • Call the Reset function, then dispatch it.
                            
    import { passwordForget } from 'enl-redux/actions/authActions';
    const mapDispatchToProps = dispatch => ({
      handleForgotPwd: bindActionCreators(passwordForget, dispatch)
    });
                            
                          
  • Send email value
                            
    submitForm(values) {
      setTimeout(() => {
        this.setState({ valueForm: values });
        console.log(`You submitted:\n\n${this.state.valueForm}`); // eslint-disable-line
        this.props.handleForgotPwd(this.state.valueForm.get('email')); // eslint-disable-line
      }, 500); // simulate server latency
    }                        
                            
                          

Component

  • File: app\components\Forms\ResetForm.js
  • Get email value
                            
    <form onSubmit={handleSubmit}>
      <div>
        <FormControl className={classes.formControl}>
          <Field
            name="email"
            component={TextField}
            placeholder={intl.formatMessage(messages.loginFieldEmail)}
            label={intl.formatMessage(messages.loginFieldEmail)}
            required
            validate={[required, email]}
            className={classes.field}
          />
        </FormControl>
      </div>
      <div className={classes.btnArea}>
        <Button variant="contained" color="primary" type="submit">
          <FormattedMessage {...messages.resetButton} />
          <ArrowForward className={classNames(classes.rightIcon, classes.iconSmall, classes.signArrow)} disabled={submitting || pristine} />
        </Button>
      </div>
    </form>