Spencer FengSpencer Feng

Category: javascript

Attach multiple event handlers to an event in Vue.js

Vue.js is a very powerful JavaScript framework which gained lots of popularity in 2017 and I think it will continue to be used in more and more projects this year, so it really is the time to learn Vue.js. In this tutorial, we will learn how to attach multiple event handlers to an event in Vue.js.

In this tutorial, we will build a simple application which displays the current Bitcoin price and the time when the price is displayed when the user clicks on the ‘Show Current Price’ button. Please note: this is not a real-life application and it will not make API calls to get the Bitcoin price, but just uses hard-coded sample data. You can see how it work in the animated image below:

We will build this application using vue-cli. If you are not familiar with vue-cli, please read the instruction from here. Basically, vue-cli is a simple CLI tool to help you quickly scaffold Vue.js projects with opinionated, battery-included build setups.

Once the project is created with vue-cli, we will remove the existing ‘HelloWord.vue’ component in the ‘src/components’ directory and then create a component called ‘BitcoinPrice’ with the code below:

<template>
  <div>
    <div>
      <strong>Current Bitcoin price is: </strong>{{ pricePool[currentPriceIndex] }} AUD
    </div>
    <div>
      <button @click="getCurrentPrice">Show Current Price</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'BitcoinPrice',
  data () {
    return {
      pricePool: [
        '13676.86',
        '15764.21',
        '19439.80',
        '18905.46',
        '12897.65',
      ],
      currentPriceIndex: 0,
    }
  },
  methods: {
    getCurrentPrice() {
      let requestResultStatusSwitch = Math.floor(Math.random() * 2);

      // succeeded
      if (requestResultStatusSwitch === 1) {
        this.currentPriceIndex++;
        if (this.currentPriceIndex == this.pricePool.length) {
          this.currentPriceIndex = 0;
        }
        this.currentPrice = this.pricePool[this.currentPriceIndex];

        this.$emit('price-request-returned', {
          message: 'The current Bitcoin price has been successfully updated.',
          success: true,
        });
      }

      // failed
      if (requestResultStatusSwitch === 0) {
        this.$emit('price-request-returned', {
          message: 'Sorry, something is wrong. Please try again later.',
          success: false,
        });
      }

    }
  },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

As you can see, this component contains the template to display the current Bitcoin price as well as the ‘Show Current Price’ button. In this component, we simulate API requests to get the current Bitcoin price. We also simulate the situation where requests might fail. Once the response returns, the application emits a custom event called ‘price-request-returned’ with a JSON object. The JSON object contains 2 keys. One is ‘message’ and the other is ‘success’. The values of these 2 keys in a successful response are different from those in a failed response, so that we can use them to decide what alert type to use later on when displaying the successful message or failed message to the user.

Now let’s open the ‘src/App.vue’ file and replace the existing code with the code below:

<template>
  <div id="app">
    <img id="logo" src="./assets/logo.png">
    <div class="alert" :class="[alertTypeClass]" v-if="alert.visible">
      {{ alert.message }}
    </div>
    <div class="updated-at">Last updated at {{ updatedAt }}</div>
    <BitcoinPrice @price-request-returned="displayAlertMessage($event); updateTime($event)"/>
  </div>
</template>

<script>
import BitcoinPrice from './components/BitcoinPrice'
import * as moment from 'moment'

export default {
  name: 'App',
  data() {
    return {
      alert: {
        message: '',
        type: '',
        visible: false,
      },
      updatedAt: moment().format('DD/MM/YYYY hh:mm:ss'),
    }
  },
  computed: {
    alertTypeClass() {
      return `alert-${this.alert.type}`;
    }
  },
  components: {
    BitcoinPrice
  },
  methods: {
    displayAlertMessage(data) {
      this.alert.message = data.message;
      this.alert.type = data.success ? 'success' : 'danger';
      this.alert.visible = true;
    },
    updateTime(data) {
      if (data.success) {
        this.updatedAt = moment().format('DD/MM/YYYY hh:mm:ss');
      }
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
#logo {
  margin-bottom: 30px;
}
.alert {
  border: 1px solid transparent;
  border-radius: 5px;
  text-align: center;
  padding: 5px 12px;
  margin-bottom: 30px;
}
.alert.alert-success {
  color: #155724;
  background-color: #d4edda;
  border-color: #c3e6cb;
}
.alert.alert-danger {
  color: #721c24;
  background-color: #f8d7da;
  border-color: #f5c6cb;
}
.updated-at {
  font-size: 11px;
  background: #ff9c44;
  color: #ffffff;
  width: 200px;
  text-align: center;
  margin: 10px auto;
  padding: 3px 0;
  border-radius: 3px;
}

</style>

Line 8 shows how we attach 2 event handlers to the custom ‘price-request-returned’ event emitted from the ‘BitcoinPrice’ component. One event handler is responsible for displaying the alert message and the other is responsible for updating the time when a successful request is made. The ‘$event’ argument passed to these 2 event handlers contains the payload of the custom event.

Please note, we also use ‘moment.js’ in the application, so please install it as well.

You can view the source code for this tutorial on GitHub.

Please sign up to my Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 10

Welcome back! This tutorial will be the last one in this series. In this tutorial, we will retrieve and display member news on the member news page. This tutorial will be broken down into following parts:

  • Create the route which handles the request for member only news
  • Create the memberNews reducer so that we can store member news as a state in the Redux store
  • Create the actions which respond to the member news request event
  • Update the MemberNewPage component so that it can trigger the event of making the request for member news

Create the route which handles the request for member only news

Let’s open the ‘app.js’ file and add the code below to it:

// get member news
app.post('/member-news', function(req, res, nex) {

    res.setHeader("Access-Control-Allow-Origin", "*");

    const token = req.body.token;

    jwt.verify(token, process.env.JWT_KEY, function(err, payload) {

        if (err) {
            return res.status(403).json({
                success: false,
                message: err.message
            });
        }

        const userId = payload.sub;

        User.findOne({
            '_id': userId
        }, function(err, user) {
            if (err) {
                return res.status(400).json({
                    success: false,
                    message: err.message
                });
            }

            if (user) {
                return res.json({
                    success: true,
                    message: 'The user has access to member news',
                    data: {
                        news: 
                            [
                                {
                                    title: 'member news 1 from the server',
                                    body: 'body of member news 1 from the server',
                                },
                                {
                                    title: 'member news 2 from the server',
                                    body: 'body of member news 2 from the server',
                                },
                                {
                                    title: 'member news 3 from the server',
                                    body: 'body of member news 3 from the server',
                                },
                                {
                                    title: 'member news 4 from the server',
                                    body: 'body of member news 4 from the server',
                                },
                                {
                                    title: 'member news 5 from the server',
                                    body: 'body of member news 5 from the server',
                                }
                            ]
                    }
                });
            } else {
                return res.status(401).json({
                    success: false,
                    message: 'The user does not have access to member news'
                });
            }
        });

    });

});

In the above code, we created a route ‘/member-news’ which handles the post request from the client side. The route retrieves the JWT token from the request body and then it verifies the token. If the token is valid, it then tries to find the corresponding user in the database. If the user exists, it returns the JSON object which contains member news.

Create the memberNews reducer so that we can store member news as a state in the Redux store

Member news returned from the server will be used in the member news page and it may also be used in other places in the future. So, it’s better to save it to our application state.  Let’s create a reducer for in the ‘client/src/reducers.js’ file:

export const memberNews = (state, action) => {

    switch (action.type) {
        case 'SET_MEMBER_NEWS': 
            return action.data;
        default: 
            return state || [];
    }

}

Create the actions which respond to the member news request event

Let’s open the ‘client/src/actions.js’ file and put the code below to it:

export const getMemberNews = (token, history) => {

    return dispatch => {
        fetch('http://localhost:3000/member-news', {
            method: 'post',
            mode: 'cors',
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
            },
            body: `token=${token}`
        })
        .then(response => {
            if (response.status >= 200 && response.status < 300) {
                return Promise.resolve(response);
            } else {
                return Promise.reject(new Error(response.statusText));
            }
        })
        .then(response => {
            return response.json();
        })
        .then(data => {
            if (data.success === true) {
                // Update the store state
                dispatch(setMemberNews(data.data.news));
            } else {
                 // Redirect to home page
                 history.push('/');
            }
        })
        .catch(err => {
            console.log(err);
        });
    }

};

export const setMemberNews = (news) => ({ type: 'SET_MEMBER_NEWS', data: news });

As you can see, in the above code, we created 2 actions: getMemberNews and setMemberNews. getmemberNews is an async action which makes a post request to the route we created earlier in this tutorial. Once we get the member news data from the server, we dispatch the setMemberNews action which passes the member news data to the memberNews reducer we created earlier in this tutorial.

Update the MemberNewsPage component so that it can trigger the event of making the request for member news

The MemberNewsPage component is responsible for following things:

  • Redirect users who are not logged in to the home page
  • Display member news for logged-in users

So, it needs to have the access to following data:

  • The current user in the application state
  • The member news in the application state

and it needs to do following things:

  • Dispatch the getMemberNews action

Based on our analysis, we need to create a container component and a presentational component for the member news page.

Let’s create the file ‘client/src/components/MemberNewsPageContainer.js’ and put the code below to it:

import React from 'react';
import { connect } from 'react-redux';
import { getMemberNews } from '../actions';
import MemberNewsPage from './MemberNewsPage';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser,
    memberNews: state.memberNews
});

const mapDispatchToProps = (dispatch) => ({
    retrieveMemberNews: (token, history) => {
        dispatch(getMemberNews(token, history));
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(MemberNewsPage);

Then, we replace the existing code in ‘client/src/components/MemberNewsPage.js’ with the code below:

import React, { Component } from 'react';
import { IsEmpty } from '../helpers';
import { memberNews } from '../reducers';

class MemberNewsPage extends Component {

    componentDidMount() {

        // Get the token in localStorage,
        // then dispatch the action to get the member only information.
        const token = localStorage.getItem('token');

        if (token !== null) {
            this.props.retrieveMemberNews(token, this.props.history);
        } else {
            this.props.history.push('/');
        }

    }

    render() {

        return (

            <div className="row">
                <div className="col col-md-3"></div>
                <div className="col col-md-6">
                    <div className="panel panel-info">
                        <div className="panel-heading">Member News</div>
                        <div className="panel-body">
                            { this.props.memberNews.length > 0 ?
                                <ul>
                                    {this.props.memberNews.map(function(newsItem, index) {
                                        return <li key={index}>{newsItem.title}</li>;
                                    })}
                                </ul>
                                :
                                <div>Sorry, there are no news for members.</div>
                            }
                        </div>
                    </div>
                </div>
                <div className="col col-md-3"></div>
            </div>

        );

    }

}

export default MemberNewsPage;

The code above is very straightforward. We try to get the JWT token in localStorage after MemberNewsPage component is mounted. If the token exists, we dispatch the getMemberNews action, otherwise we send the user to the home page.

Next, we need to use MemberNewsPageContainer component in our route instead of using MemberNewsPage component. So let’s open ‘client/src/App.js’ file and replace

import MemberNewsPage from './components/MemberNewsPage';

with

import MemberNewsPage from './components/MemberNewsPageContainer';

Then, we can add the member news page to the navigation menu as an item in the dropdown menu section for members. So let’s open ‘client/src/components/Navbar.js’ file and add the code below to it:

<li><Link to='/member-news'>Member News</Link></li>

Now, if you login and then go to the member news page, you can see that a list of member news items is showing up. But, if you do not login and try to access that page, you will be redirected to the home page.

Thank you for following along the series which talks about how we can use JSON Web Tokens for authentication and authorisation in React apps.

View the source code for this post on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Welcome back! This tutorial will be the last one in this series. In this tutorial, we will retrieve and display member news on the member news page. This tutorial will be broken down into following parts:

  • Create the route which handles the request for member only news
  • Create the memberNews reducer so that we can store member news as a state in the Redux store
  • Create the actions which respond to the member news request event
  • Update the MemberNewPage component so that it can trigger the event of making the request for member news

Create the route which handles the request for member only news

Let’s open the ‘app.js’ file and add the code below to it:

// get member news
app.post('/member-news', function(req, res, nex) {

    res.setHeader("Access-Control-Allow-Origin", "*");

    const token = req.body.token;

    jwt.verify(token, process.env.JWT_KEY, function(err, payload) {

        if (err) {
            return res.status(403).json({
                success: false,
                message: err.message
            });
        }

        const userId = payload.sub;

        User.findOne({
            '_id': userId
        }, function(err, user) {
            if (err) {
                return res.status(400).json({
                    success: false,
                    message: err.message
                });
            }

            if (user) {
                return res.json({
                    success: true,
                    message: 'The user has access to member news',
                    data: {
                        news: 
                            [
                                {
                                    title: 'member news 1 from the server',
                                    body: 'body of member news 1 from the server',
                                },
                                {
                                    title: 'member news 2 from the server',
                                    body: 'body of member news 2 from the server',
                                },
                                {
                                    title: 'member news 3 from the server',
                                    body: 'body of member news 3 from the server',
                                },
                                {
                                    title: 'member news 4 from the server',
                                    body: 'body of member news 4 from the server',
                                },
                                {
                                    title: 'member news 5 from the server',
                                    body: 'body of member news 5 from the server',
                                }
                            ]
                    }
                });
            } else {
                return res.status(401).json({
                    success: false,
                    message: 'The user does not have access to member news'
                });
            }
        });

    });

});

In the above code, we created a route ‘/member-news’ which handles the post request from the client side. The route retrieves the JWT token from the request body and then it verifies the token. If the token is valid, it then tries to find the corresponding user in the database. If the user exists, it returns the JSON object which contains member news.

Create the memberNews reducer so that we can store member news as a state in the Redux store

Member news returned from the server will be used in the member news page and it may also be used in other places in the future. So, it’s better to save it to our application state.  Let’s create a reducer for in the ‘client/src/reducers.js’ file:

export const memberNews = (state, action) => {

    switch (action.type) {
        case 'SET_MEMBER_NEWS': 
            return action.data;
        default: 
            return state || [];
    }

}

Create the actions which respond to the member news request event

Let’s open the ‘client/src/actions.js’ file and put the code below to it:

export const getMemberNews = (token, history) => {

    return dispatch => {
        fetch('http://localhost:3000/member-news', {
            method: 'post',
            mode: 'cors',
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
            },
            body: `token=${token}`
        })
        .then(response => {
            if (response.status >= 200 && response.status < 300) {
                return Promise.resolve(response);
            } else {
                return Promise.reject(new Error(response.statusText));
            }
        })
        .then(response => {
            return response.json();
        })
        .then(data => {
            if (data.success === true) {
                // Update the store state
                dispatch(setMemberNews(data.data.news));
            } else {
                 // Redirect to home page
                 history.push('/');
            }
        })
        .catch(err => {
            console.log(err);
        });
    }

};

export const setMemberNews = (news) => ({ type: 'SET_MEMBER_NEWS', data: news });

As you can see, in the above code, we created 2 actions: getMemberNews and setMemberNews. getmemberNews is an async action which makes a post request to the route we created earlier in this tutorial. Once we get the member news data from the server, we dispatch the setMemberNews action which passes the member news data to the memberNews reducer we created earlier in this tutorial.

Update the MemberNewsPage component so that it can trigger the event of making the request for member news

The MemberNewsPage component is responsible for following things:

  • Redirect users who are not logged in to the home page
  • Display member news for logged-in users

So, it needs to have the access to following data:

  • The current user in the application state
  • The member news in the application state

and it needs to do following things:

  • Dispatch the getMemberNews action

Based on our analysis, we need to create a container component and a presentational component for the member news page.

Let’s create the file ‘client/src/components/MemberNewsPageContainer.js’ and put the code below to it:

import React from 'react';
import { connect } from 'react-redux';
import { getMemberNews } from '../actions';
import MemberNewsPage from './MemberNewsPage';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser,
    memberNews: state.memberNews
});

const mapDispatchToProps = (dispatch) => ({
    retrieveMemberNews: (token, history) => {
        dispatch(getMemberNews(token, history));
    }
});

export default connect(mapStateToProps, mapDispatchToProps)(MemberNewsPage);

Then, we replace the existing code in ‘client/src/components/MemberNewsPage.js’ with the code below:

import React, { Component } from 'react';
import { IsEmpty } from '../helpers';
import { memberNews } from '../reducers';

class MemberNewsPage extends Component {

    componentDidMount() {

        // Get the token in localStorage,
        // then dispatch the action to get the member only information.
        const token = localStorage.getItem('token');

        if (token !== null) {
            this.props.retrieveMemberNews(token, this.props.history);
        } else {
            this.props.history.push('/');
        }

    }

    render() {

        return (

            <div className="row">
                <div className="col col-md-3"></div>
                <div className="col col-md-6">
                    <div className="panel panel-info">
                        <div className="panel-heading">Member News</div>
                        <div className="panel-body">
                            { this.props.memberNews.length > 0 ?
                                <ul>
                                    {this.props.memberNews.map(function(newsItem, index) {
                                        return <li key={index}>{newsItem.title}</li>;
                                    })}
                                </ul>
                                :
                                <div>Sorry, there are no news for members.</div>
                            }
                        </div>
                    </div>
                </div>
                <div className="col col-md-3"></div>
            </div>

        );

    }

}

export default MemberNewsPage;

The code above is very straightforward. We try to get the JWT token in localStorage after MemberNewsPage component is mounted. If the token exists, we dispatch the getMemberNews action, otherwise we send the user to the home page.

Next, we need to use MemberNewsPageContainer component in our route instead of using MemberNewsPage component. So let’s open ‘client/src/App.js’ file and replace

import MemberNewsPage from './components/MemberNewsPage';

with

import MemberNewsPage from './components/MemberNewsPageContainer';

Then, we can add the member news page to the navigation menu as an item in the dropdown menu section for members. So let’s open ‘client/src/components/Navbar.js’ file and add the code below to it:

<li><Link to='/member-news'>Member News</Link></li>

Now, if you go to the member news page as a logged-in user, you can see that member news are retrieved from the server and are displayed on this page. But if you try to access this page as a not-logged-in user, you will be redirected to the home page.

You can view the source code for this tutorial on GitHub.

Thank you for following along this series and hope you will find something useful for your own project.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 9

Welcome to the ninth tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. Currently, the user is able to create an account, login to and logout from our application, however, there is one thing that we have missed which is that a logged-in user can still access the login and signup page. In this tutorial, we will complete following tasks:

  • Redirect logged-in users to the home page if they try to access the login page
  • Redirect logged-in users to the home page if they try to access the signup page

Redirect logged-in users to the home page if they try to access the login page

We will use the Redirect component from react-router-dom to redirect logged-in users to the home page when they try to access the login page. Let’s open ‘client/src/App.js’ file and import Redirect component from react-router-dom by updating the code below

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

to

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

After importing Redirect component into ‘client/src/App.js’, let’s replace the code below

<Route path="/login" component={LoginPage} />

with

<Route path="/login" render={() => (
    ! IsEmpty(this.props.store.getState().cUser) ? (
        <Redirect to="/" />
    ) : (
        <LoginPage />
    )
)} />

What the change does is very obvious: if the cUser object in the state is empty, the application redirects the user to the home page, otherwise, the application loads the login page.

Redirect logged-in users to the home page if they try to access the signup page

This is very similar to what we did in the previous section. Let’s replace the code in ‘client/src/App.js’

<Route path="/signup" component={SignupPage} />

with

<Route path="/signup" render={() => (
    ! IsEmpty(this.props.store.getState().cUser) ? (
        <Redirect to="/" />
    ) : (
        <SignupPage />
    )
)} />

After making those 2 changes in ‘client/src/App.js’, now logged-in users will be redirected to home page when they try to access ‘login’ and ‘signup’ pages.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 8

Welcome to the eighth tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. Currently, the logout link does nothing in our application. In this tutorial, we will add the function to log a logged-in user out which includes tasks:

  • Clear the JWT token in the localStorage and empty the cUser object in the state
  • Redirect the user to the home page

Clear the JWT token in the localStorage and empty the cUser object in the state

Once the logout link is clicked, the token in the localStorage needs to be cleared, so that it can no longer be used to authenticate a user. Let’s update the code in ‘client/src/Navbar.js’ to the code below (changes are highlighted):

import React, { Component } from 'react';
import { IsEmpty } from '../helpers';
import { Link } from 'react-router-dom';

class Navbar extends Component {
    constructor(props) {
        super(props);
        this.logout = this.logout.bind(this);
    }

    logout(e) {
        e.preventDefault();

        // set store state cUser to be an empty object
        this.props.removeLoggedInUser();

        // remove the token in localStorage
        localStorage.removeItem('token');
    }

    render() {

        return (

            <nav className="navbar navbar-inverse navbar-fixed-top">
                <div className="container">
                    <div className="navbar-header">
                        <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                            <span className="sr-only">Toggle navigation</span>
                            <span className="icon-bar"></span>
                            <span className="icon-bar"></span>
                            <span className="icon-bar"></span>
                        </button>
                        <a className="navbar-brand" href="#">React JWT</a>
                    </div>
                    <div id="navbar" className="collapse navbar-collapse">
                        <ul className="nav navbar-nav">
                            <li className="active"><Link to='/'>Home</Link></li>
                        </ul>
                        <ul className="nav navbar-nav navbar-right">
                            {IsEmpty(this.props.cUser) && 
                                <li><Link to='/login'>Login</Link></li>
                            }
                            {IsEmpty(this.props.cUser) &&
                                <li><Link to='/signup'>Signup</Link></li>
                            }
                            {!IsEmpty(this.props.cUser) &&
                                <li className="dropdown">
                                    <a className="dropdown-toggle" href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                                        Hello, {this.props.cUser.firstName} {this.props.cUser.lastName} <span className="caret"></span>
                                    </a>
                                    <ul className="dropdown-menu">
                                        <li><a href="#" onClick={this.logout}>Logout</a></li>
                                    </ul>
                                </li>
                            }
                        </ul>
                    </div>
                </div>
            </nav>

        )

    }

}

export default Navbar;

The changes are obvious. We added an event listener to handle the click event of the logout link. In the event handler, we called the ‘removeLoggedInUser()’ function which is passed in as a component prop to empty the logged-in user and then we clear the token in the local storage.

As we mentioned above, we need a ‘removeLoggedInUser()’ function to empty the logged-in user. Let’s open the ‘client/src/components/NavbarContainer.js’ file and update it to the code below (changes are highlighted):

import React from 'react';
import { connect } from 'react-redux';
import Navbar from './Navbar';
import { setAuthenticatedUser } from '../actions';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser
});

const mapDispatchToProps = (dispatch) => ({
    removeLoggedInUser: () => { dispatch(setAuthenticatedUser({})); }
});

export default connect(mapStateToProps, mapDispatchToProps)(Navbar);

The changes we made to this file allow us to pass a function which dispatches the ‘setAuthenticatedUser’ action to the presentational component as a prop. When this action is dispatched, the cUser state in the store becomes an empty object.

Redirect the user to the home page

We want to redirect users to the home page after they logged out. But as you can see, the logout link is defined in the ‘Navbar’ component which is not a “route component “, meaning it is not rendered like so: <Route path=”/your-path” component={YourComponent} />, so it does not have the access to the Router history object which we need to preform the redirection like a “route component”. In order to allow the ‘Navbar’ component to have the access to the Router history object, we need to use the ‘withRouter’ function in react-router. If you want to learn more about it, please read the documentation here.

Let’s update the ‘client/src/components/NavbarContainer’ to the code below (changes are highlighted):

import React from 'react';
import { connect } from 'react-redux';
import Navbar from './Navbar';
import { setAuthenticatedUser } from '../actions';
import { withRouter } from 'react-router-dom';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser
});

const mapDispatchToProps = (dispatch) => ({
    removeLoggedInUser: () => { dispatch(setAuthenticatedUser({})); }
});

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Navbar));

Then add the following code to the bottom of the ‘logout’ function in ‘client/src/components/Navbar.js’ file:

// redirect to home page
this.props.history.push('/');

In order to test if the redirection work, let’s create a page component ‘client/src/components/MemberNewsPage.js’ with the contents below:

import React, { Component } from 'react';

class MemberNewsPage extends Component {

    render() {

        return (

            <div className="row">
                <div className="col col-md-3"></div>
                <div className="col col-md-6">
                    <div className="panel panel-info">
                        <div className="panel-heading">Member News</div>
                        <div className="panel-body">
                            <ul>
                                <li>Member news 1</li>
                                <li>Member news 2</li>
                                <li>Member news 3</li>
                                <li>Member news 4</li>
                                <li>Member news 5</li>                    
                            </ul>
                        </div>
                    </div>
                </div>
                <div className="col col-md-3"></div>
            </div>

        );

    }

}

export default MemberNewsPage;

Let’s import the page component to the ‘client/src/App.js’ file:

import MemberNewsPage from './components/MemberNewsPage';

Then, we add it to the route.

<Route path="/" component={HomePage} />

Now, if we login to the application and then go to the member news page and then click on the logout link, you can see that we can logout the application and then we will be redirected to the home page.

Great, that is all we need to cover in this tutorial. In the next tutorial in this series, we will cover how to prevent logged-in users from accessing login and logout pages. See you next time!

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 7

Welcome to the seventh tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. Users have been able to signup and login to the application and their login status can be displayed. But, if you refresh the web page when you have logged in, you will see that you are not logged in anymore. So, in this tutorial, we will persist a user’s login status after the web page is refreshed.

  • Create the route on the server side to authenticate a user by a JSON Web Token
  • Create our Redux actions to update the app state after a user is authenticated by a JSON Web Token
  • Dispatch the action

Create the route on the server side to authenticate a user by a JSON Web Token

The code below creates the route which authenticates a user via JWT and please add it to the ‘app.js’ file.

// create the route to handle user authentication via JWT
app.post('/authenticate', function(req, res, nex) {

    res.setHeader("Access-Control-Allow-Origin", "*");

    const token = req.body.token;

    jwt.verify(token, process.env.JWT_KEY, function(err, payload) {

        if (err) {
            return res.status(403).json({
                success: false,
                message: err.message
            });
        }

        const userId = payload.sub;

        User.findOne({
            '_id': userId
        }, function(err, user) {
            if (err) {
                return res.status(400).json({
                    success: false,
                    message: err.message
                });
            }

            if (user) {
                return res.json({
                    success: true,
                    message: 'The user was found',
                    user: {
                        _id: user._id,
                        firstName: user.firstName,
                        lastName: user.lastName,
                        email: user.email
                    }
                });
            } else {
                return res.status(404).json({
                    success: false,
                    message: 'The user could not be found'
                });
            }
        });

    });

});

It does following things:

  • It verifies the token passed from the request
  • If the token is valid, it retrieves the _id value from the token for the user object
  • Then, it gets the User object which has this _id value from the database
  • If this user exists in the database, it returns a JSON object with the detailed information of this user.

Create our Redux actions to update the app state after a user is authenticated by a JSON Web Token

Now, we have created the route. The next step is to create the Redux actions which can be deployed to update the app state when a user is authenticated via a JSON Web Token.

Let’s add the code below to the ‘client/src/actions.js’ file:

export const authenticateUser = (token) => {

    return dispatch => {
        fetch('http://localhost:3000/authenticate', {
            method: 'post',
            mode: 'cors',
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
            },
            body: `token=${token}`
        })
        .then(response => {
            if (response.status >= 200 && response.status < 300) {
                return Promise.resolve(response);
            } else {
                return Promise.reject(new Error(response.statusText));
            }
        })
        .then(response => {
            return response.json();
        })
        .then(data => {
            console.log(data);
            if (data.success === true) {
                // Update the store state
                dispatch(setAuthenticatedUser(data.user));
            }
        })
        .catch(err => {
            console.log(err);
        });
    }
};

The code above is very obvious. It creates an asynchronous action. First, it sends a POST request to the server with the token. Then, if the response from the server is correct, it dispatches the ‘setAuthenticatedUser’ action to update the state of cUser.

Dispatch the action

The action needs to be dispatched automatically every time a page is refreshed no matter what the page is, so the perfect place is the ‘componentDidMount’ event in the ‘App’ component. Let’s add following code to ‘client/App.js’ file.

import { authenticateUser } from './actions';
import { IsEmpty } from './helpers';
componentDidMount() {

    // check if there is a logged in user
    // if there is no logged in user, check to see if there is a token item in localstorage
    // if there is a token item in localstorage, verify if this token is correct and get the its associated user
    if (IsEmpty(this.props.store.getState().cUser)) {

        const token = localStorage.getItem('token');

        if (token !== null) {
            this.props.store.dispatch(authenticateUser(token));
        }

    }

}

The code in the ‘codeDidMount()’ function is executed every time the ‘App’ component is mounted. The Redux store object is passed to the ‘App’ component as a component prop. If the cUser object in the store object is not empty, it dispatches the ‘authenticateUser’ action.

Now, if you refresh the page after you have logged in, you can see you are still logged in.

Let’s call it a day and in the next tutorial in this series, we will work on the logout process. See you next time.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 6

Welcome to the sixth tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. So far, our app has been able to handle user signup and user login. But there is no way to tell if a user is logged in or not without checking if there is a JWT in the browser’s localStorage. In this tutorial, we have following tasks, so that the users can tell their login status very easily.

  • Show correct navigation menu items and information in the navigation menu bar

Show correct navigation menu items and information in the navigation menu bar

The first thing we need to do is to hide the ‘Login’ and ‘Signup’ menu items from logged-in users. The navigation menu is managed in the ‘Navbar’ component. In order for the ‘Navbar’ component to be aware of user login status, we need to pass the store state to it.  So, we need to separate the ‘Navbar’ component into a container component which can get the store state and a presentational component which can get the store state as a prop passed from the container component.

Let’s create the container component first. Please create the ‘client/src/components/NavbarContainer.js’ file and put the code below inside it.

import React from 'react';
import { connect } from 'react-redux';
import Navbar from './Navbar';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser
});

export default connect(mapStateToProps, null)(Navbar);

Then we need to go to ‘client/src/App.js’ to replace

import Navbar from './components/Navbar';

with

import Navbar from './components/NavbarContainer';

The existing ‘Navbar.js’ will be used as the presentational component and now it can access the cUser object in the store from the cUser prop. Things are pretty straightforward here now. If the cUser object is empty, it means that there is not a logged-in user and then we need to hide the welcome message and the logout link. Otherwise, it means there is a logged-in user and we need to hide the ‘Login’ and ‘Signup’ menu items and display the welcome message and the logout link.

It looks like we need to check if the cUser object is empty or not in several place, I think it’s better to create a helper function which checks if a function is empty or not.

So let’s create the ‘client/src/helpers.js’ file and put the helper function inside it.

export function isEmpty(obj) {

    for(var key in obj) {
        if(obj.hasOwnProperty(key))
            return false;
    }
    return true;

}

After creating the helper function, we are going to import it to ‘client/src/components/Navbar.js’ and use it to decide when to display and hide some navigation menu items.

Let’s replace the code in ‘client/src/components/Navbar.js’ with the code below:

import React, { Component } from 'react';
import { IsEmpty } from '../helpers';
import { Link } from 'react-router-dom';

class Navbar extends Component {

    render() {

        return (

            <nav className="navbar navbar-inverse navbar-fixed-top">
                <div className="container">
                    <div className="navbar-header">
                        <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                            <span className="sr-only">Toggle navigation</span>
                            <span className="icon-bar"></span>
                            <span className="icon-bar"></span>
                            <span className="icon-bar"></span>
                        </button>
                        <a className="navbar-brand" href="#">React JWT</a>
                    </div>
                    <div id="navbar" className="collapse navbar-collapse">
                        <ul className="nav navbar-nav">
                            <li className="active"><Link to='/'>Home</Link></li>
                        </ul>
                        <ul className="nav navbar-nav navbar-right">
                            {IsEmpty(this.props.cUser) && 
                                <li><Link to='/login'>Login</Link></li>
                            }
                            {IsEmpty(this.props.cUser) &&
                                <li><Link to='/signup'>Signup</Link></li>
                            }
                            {!IsEmpty(this.props.cUser) &&
                                <li className="dropdown">
                                    <a className="dropdown-toggle" href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                                        Hello, {this.props.cUser.firstName} {this.props.cUser.lastName} <span className="caret"></span>
                                    </a>
                                    <ul className="dropdown-menu">
                                        <li><a href="#">Logout</a></li>
                                    </ul>
                                </li>
                            }
                        </ul>
                    </div>
                </div>
            </nav>

        )

    }

}

export default Navbar;

The changes include:

  • Import our ‘IsEmpty()’ helper function
  • Wrap the ‘Login’ menu item in a conditional statement and it is only rendered if the cUser object is empty
  • Wrap the ‘Signup’ menu item in a conditional statement and it is only rendered if the cUser object is empty
  • Wrap the welcome message and the logout link in a conditional statement and they are only rendered if the cUser object is empty
  • Display the logged-in user’s first name and last name.

It is great that users can tell their login status very easily now.

Let’s call it a day. In the next tutorial in this series, we will handle the situation where we need to keep the users’ login status after they refresh the page. See you next time.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 5

This is the fifth tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. In this tutorial, we will work on the login process:

  • Create the route on the server side to handle user login
  • Create our Redux actions to update the app state after a user logged in
  • Create React components for the login form

Create the route on the server side to handle user login

Like what we did with user signup, we will utilise the ‘passport’ package to create the route to handle user login.

First, please find below the named strategy called ‘local-signin’ using passport LocalStrategy which is added to the ‘app.js’ file.

// create the 'local-signin' named strategy
passport.use('local-signin', new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true,
    session: false
},
function(req, username, password, done) {
    User.findOne({
        'email': username
    }, function(err, user) {
        if (err) {
            return done(err);
        }

        // if no user is found, return 
        if (!user) {
            return done(null, false);
        }

        // if the user is found, but the password is wrong, return
        if (! user.validPassword(password)) {
            return done(null, false);
        }

        // create a jwt
        const payload = {
            sub: user._id
        }
        const token = jwt.sign(payload, process.env.JWT_KEY);

        const data = {
            _id: user._id,
            email: user.email,
            firstName: user.firstName,
            lastName: user.lastName
        };

        return done(null, token, data);

    });
}));

It is very similar to the ‘local-signup’ named strategy which we created in the previously. The only difference is that it does not create a new user, instead, it finds the user with the provided username and password and creates the token.

Next, we create the route to handle the sign in request from the client site. Please add the code below to the ‘app.js’ file.

// create the route to handle user signin
app.post('/signin', function(req, res, next) {

    res.setHeader("Access-Control-Allow-Origin", "*");

    passport.authenticate('local-signin', function(err, token, userData) {

        if (err) {
            return res.status(400).json({
                success: false,
                message: err.message
            });
        }

        return res.json({
            success: true,
            message: 'You have succesfully signed in',
            token: token,
            user: userData
        });

    })(req, res, next);

});

Create our Redux actions to update the app state after a user logged in

Let’s create the ‘userLogin’ action in the ‘client/src/actions.js’ file:

export const userLogin = (user, history) => {
    return dispatch => {
        fetch('http://localhost:3000/signin', {
            method: 'post',
            mode: 'cors',
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8" 
            },
            body: `email=${user.email}&password=${user.password}`
        })
        .then(response => {
            if (response.status >= 200 && response.status < 300) {
                return Promise.resolve(response);
            } else {
                return Promise.reject(new Error(response.statusText));
            }
        })
        .then(response => response.json())
        .then(data => {
            console.log(data);
            if (data.success === true) {
                // Save the token to local storage
                localStorage.setItem('token', data.token);

                // Update the store state
                dispatch(setAuthenticatedUser(data.user));

                // Redirect to user logged-in landing page
                history.push('/');
            }
        })
        .catch(err => {
            console.log(err);
        });
    }
}

Create React components for the sign in form

Let’s create the container component first. Create a file called ‘LoginPageContainer.js’ in the ‘client/src/components’ directory with the code below:

import React from 'react';
import { connect } from 'react-redux';
import { userLogin } from '../actions';
import LoginPage from './LoginPage';

const mapDispatchToProps = (dispatch) => ({
    signin: (user, history) => { 
        dispatch(userLogin(user, history)); 
    }
});

export default connect(null, mapDispatchToProps)(LoginPage);

Like the ‘SignupPage.js’, the existing ‘LoginPage.js’ will be the presentational component with some changes. Let’s replace the code in ‘client/src/components/LoginPage.js’ with the code below:

import React, { Component } from 'react';

class LoginPage extends Component {
    constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleSubmit(e) {
        e.preventDefault();

        console.log(`email: ${this.email.value}`);
        console.log(`password: ${this.password.value}`);

        const user = {
            email: this.email.value,
            password: this.password.value
        };

        this.props.signin(user, this.props.history);
    }

    render() {
        return (
            <div className="row">
                <div className="col col-md-3"></div>
                <div className="col col-md-6">
                    <form onSubmit={this.handleSubmit}>
                        <div className="form-group">
                            <label htmlFor="email">Email</label>
                            <input type="text" name="email" id="email" className="form-control" ref={(email) => {this.email = email;}} />
                        </div>
                        <div className="form-group">
                        <label htmlFor="password">Password</label>
                            <input type="password" name="password" id="password" className="form-control" ref={(password) => {this.password = password;}} />
                        </div>
                        <div className="form-group">
                            <input type="submit" value="Login" />
                        </div>
                    </form>
                </div>
                <div className="col col-md-3"></div>
            </div>
        );
    }
}

export default LoginPage;

After doing this, we need to go to the ‘client/src/App.js’ to replace:

import LoginPage from './components/LoginPage';

with

import LoginPage from './components/LoginPageContainer';

Now, if we submit the login form with the correct username and password, we can see that we are redirected to the home page and an item with the key ‘token’ has been created in our local Storage object.

In the next tutorial in this series, we will enable the app to display the correct menu items based on the user’s login status. Stay tuned.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 4

This is the fourth part of our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. In this part, we will work on the signup process:

  • Create the route on the server side to handle user signup
  • Create our Redux actions to update the app state after a user signed up
  • Create React components for the signup form

Create the route on the server side to handle user signup

We will utilise the ‘passport’ package to create the endpoint on the server to handle user signup.

First, we will create a named strategy called ‘local-signup’ using passport LocalStrategy.

// create the 'local-signup' named strategy
passport.use('local-signup', new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true,
    session: false
},
function(req, username, password, done) {

    User.findOne({
        'email': username
    }, function(err, user) {
        if (err) {
            return done(err);
        }

        if (user) {
            return done(null, false);
        }

        let newUser = new User();

        newUser.email = username;
        newUser.password = newUser.generateHash(password);
        newUser.firstName = req.body.firstName;
        newUser.lastName = req.body.lastName;

        newUser.save(function(err) {
            if (err) {
                throw err;
            }

            // create a jwt
            const payload = {
                sub: newUser._id
            }
            const token = jwt.sign(payload, process.env.JWT_KEY);

            const data = {
                _id: newUser._id,
                email: newUser.email,
                firstName: newUser.firstName,
                lastName: newUser.lastName
            };

            return done(null, token, data);
        });

    });
}));

By default, passport LocalStrategy expects to find credentials in parameters named username and password, but in our application, we would like to use email instead of username, so I specified the username field explicitly in line 32 when I created the LocalStrategy instance. In addition, our signup form also contains ‘firstName’ and ‘lastName’ fields and we need to get the values of these 2 fields from the request body, so that is why we set ‘passReqToCallback’ true.

After we get the request, email and password, we check if a user with the same email already exists in the system. If yes, we return immediately with an error. If not, we will create the user and save the user to our database.

After the user is saved to the database successfully, we create the JSON Web Token using the package ‘jsonwebtoken’ which we installed in the previous post. In order to use it, we need to import the ‘jsonwebtoken’ package to the ‘app.js’ file.

var jwt = require('jsonwebtoken');

Please note, we store the key in the .env, so please do remember to include the ‘dotenv’ package at the top of the file.

require('dotenv').config();

The named strategy ‘local-signup’ has been created, so now our work is to create the route to handle the signup request sent from the client side. Please add the code below to the ‘app.js’ file.

app.post('/signup', function(req, res, next) {

    res.setHeader("Access-Control-Allow-Origin", "*");

    passport.authenticate('local-signup', function(err, token, userData) {

        if (err) {
            return res.status(400).json({
                success: false,
                message: err.message
            });
        }

        return res.json({
            success: true,
            message: 'You have successfully signed up and logged in',
            token: token,
            user: userData
        });
    })(req, res, next);
});

So, here’s the route handling the user signup request. There are a few things going on that are worth going over.

First, since our client side (the React app) has a different port than our server (the Express app), when it makes a request to the server, it is a cross-origin HTTP request. In order to give our client side app the permission to get the resources it needs, we need to set the ‘Access-Control-Allow-Origin’ header in our server’s response header. In our case, I set it to allow all domains.

Next, the callback function in the authenticate() method get called if authentication was successful.

Last, in the JSON response, we returned the token and the userData which contains the id, email, firstName and lastName of the User object.

Create our Redux actions to update the app state after a user signed up

Actions are payloads of information that send data from the application to the store. The reducer we created previously uses the data to update the application state. The action which we use to send data to the store when a user signs up needs to be an asynchronous action because we do not want to send the data to the store until we receive an answer from the server after we sent the request to the server.

We will use the Redux Thunk middleware to implement the asynchronous action for the signup event. Let’s install the redux-thunk package first:

$ npm install redux-thunk --save

After installing the redux-thunk package, we need to import it to our ‘client/src/index.js’:

import thunkMiddleware from 'redux-thunk';

Then we need to specify the middleware which is Redux Thunk middleware in our case when we create the Redux store in ‘client/src/index.js’, so we need to replace the code which we use to create the Redux store with the code below:

// Create the store
const store = createStore(combineReducers({
    cUser: reducers.cUser
}),
applyMiddleware(
    thunkMiddleware
));

Now, we are ready to create the actions. Let’s create a file ‘client/src/actions.js’ with the code below:

export const userSignup = (user, history) => {
    return dispatch => {
        fetch('http://localhost:3000/signup', {
            method: 'post',
            mode: 'cors',
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
            },
            body: `firstName=${user.firstName}&lastName=${user.lastName}&email=${user.email}&password=${user.password}`
        })
        .then(response => {
            if (response.status >= 200 && response.status < 300) {
                return Promise.resolve(response);
            } else {
                return Promise.reject(new Error(response.statusText));
            }
        })
        .then(response => {
            return response.json();
        })
        .then(data => {
            if (data.token !== false) {
                console.log(`response token: ${data.token}`);

                // Save the token to local storage
                localStorage.setItem('token', data.token);

                // Update the store state
                dispatch(setAuthenticatedUser(data.user));

                // Redirect to home page
                history.push('/');
            } else {
                // TODO
            }
        })
        .catch(err => {
            console.log(err);  
        });
    }
};

export const setAuthenticatedUser = (user) => ({ type: 'SET_USER', data: user });

As you can see, we created 2 actions here. The first action is userSignup and it is an asynchronous action which makes a request to our server and after it gets a successful response from the server, it dispatches a normal action which is setAuthenticatedUser in our case to update the application state. In the userSignup action, we also store the JWT token in localStorage in our browser, so that when a logged in user refreshes the web page before the token expires, the user can still be authenticated without having to login again.

Create React components for the signup form

We already created the React component which is ‘client/src/components/SignupPage.js’ to display the page with the signup form. But what we need is not just displaying the form, we also need to pass the action ‘userSignup’ we just created to it form the store as a prop of the React component. In order to achieve it, we need to divide our SignupPage component into a presentational component which is responsible for the how our signup page looks like (markup, styles) and a container component which is responsible for how our signup page works (data fetching, state updates).

Let’s create the container component first. Create a file called ‘SignupPageContainer.js’ in the ‘client/src/components’ directory with the code below:

import React from 'react';
import { connect } from 'react-redux';
import { userSignup } from '../actions';
import SignUpPage from './SignupPage';

const mapDispatchToProps = (dispatch) => ({
    signup: (user, history) => {
        dispatch(userSignup(user, history));
    }
});

export default connect(null, mapDispatchToProps)(SignUpPage);

What the code above does is that it allows us to pass the function which dispatches the userSignup action to the presentational component of signup page as a React component prop called ‘signup’.

Our existing ‘SignupPage.js’ will be the presentational component, but we need to make some changes to it, so that it can use the props that passed to it.

Let’s replace the code in ‘client/src/components/SignupPage.js’ with the code below:

import React, { Component } from 'react';

class SignupPage extends Component {
    constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleSubmit(e) {
        e.preventDefault();

        console.log(`email: ${this.email.value}`);
        console.log(`password: ${this.password.value}`);

        const user = {
            firstName: this.firstName.value,
            lastName: this.lastName.value,
            email: this.email.value,
            password: this.password.value
        };

        this.props.signup(user, this.props.history);
    }

    render() {
        return (
            <div className="row">
                <div className="col col-md-3"></div>
                <div className="col col-md-6">
                    <form onSubmit={this.handleSubmit}>
                        <div className="form-group">
                            <label htmlFor="firstName">First Name</label>
                            <input type="text" name="firstName" id="firstName" className="form-control" ref={(firstName) => {this.firstName = firstName;}} />
                        </div>
                        <div className="form-group">
                            <label htmlFor="lastName">Last Name</label>
                            <input type="text" name="lastName" id="lastName" className="form-control" ref={(lastName) => {this.lastName = lastName;}} />
                        </div>
                        <div className="form-group">
                            <label htmlFor="email">Email</label>
                            <input type="email" name="email" id="email" className="form-control" ref={(email) => {this.email = email;}} />
                        </div>
                        <div className="form-group">
                            <label htmlFor="password">Password</label>
                            <input type="password" name="password" id="password" className="form-control" ref={(password) => {this.password = password;}} />
                        </div>
                        <div className="form-group">
                            <input type="submit" value="Sign Up" />
                        </div>
                    </form>
                </div>
                <div className="col col-md-3"></div>
            </div>
        );
    }
}

export default SignupPage;

Basically, the updated code allows the signup page presentational component to do following things:

  1. When the signup form is submitted, it gets the value of all the fields in the form
  2. Then it creates an object with the form values
  3. Then it calls the function that was passed to it as a prop to dispatch the ‘userSignup’ action.

After doing this, we need to go to the ‘client/src/App.js’ to replace:

import SignupPage from './components/SignuPage';

with

import SignupPage from './components/SignupPageContainer';

Now, if we submit the signup form in the signup page, we can see that a new user has been created in our database and an item with the key ‘token’ has been created in our local Storage object.

In the next tutorial in this series, we will work on the sign in process. See you then.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 3

This is the third part of our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. In this part, we will do some work on our server side:

  • Setup the database connection
  • Create the User model
  • Install the Passport package and the jsonwebtoken package

Setup the database connection

I have created a sandbox database in mLab. mLab is a Database-as-a-Service for MongoDB and it offers a sandbox plan which is ideal for learning, developing and prototyping. Then, I will use Mongoose, a MongoDB object modeling tool, to establish the connection between our server and the MongoDB database.

Let’s run the command below to install the mongoose package:

$ npm install mongoose --save

When we set up the database connection, we need to provide the standard MongoDB URI which contains the sensitive information such as the username and password of our database, so it’s better to save the information in a .env file which is not committed to version control. Luckily, the dotenv package allows us just to do that.

Let’s install it by running the following command:

$ npm install dotenv --save

Then, create a .env file in the root directory and then open that file to add the MONGODB_URI variable and its value to it:

MONGODB_URI='your-standard-mongodb-uri'

The standard MongoDB URI can be grabbed from there:

Great, we have completed the preparation for connecting to the database. Then, let’s add the following code to the ‘bin/www’ file:

require('dotenv').config();
var mongoose =  require('mongoose');
// connect to the database
mongoose.connect(process.env.MONGODB_URI, { useMongoClient: true });
mongoose.Promise = global.Promise;
mongoose.connection.on('error', (err) => {
  console.error('mongoodb error: ' + err.message);
});

The first block of code requires and configures dotenv, so that process.env now has the key and value we defined in the .env file.

The second block of code requires mongoose.

The third block of code connects to our database, asks mongoose to use ES6 native promises for its async operations, like .save() and queries and display an error message if connections fails.

Create the User model

Next, we need to create the User model which is used to store the logged-in user. Models are defined by passing a Schema instance to mongoose.model. Let’s create a folder called ‘models’ in the root directory and then create a file called ‘User.js’ with the code below.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');

// create a schema
const userSchema = new Schema({
    firstName: {
        type: String,
        required: true
    },
    lastName: {
        type: String,
        required: true 
    },
    email: {
        type: String,
        required: true 
    },
    password: {
        type: String,
        required: true
    }
});

// generating a hash
userSchema.methods.generateHash = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// checking if password is correct
userSchema.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.password);
};

module.exports = mongoose.model('User', userSchema);

As you can see, in the above code, we create a model with 4 required properties and they are ‘firstName’, ‘lastName’, ’email’ and ‘password’. We also a method to hash the password and a method to validate the password which utilise the ‘bcrypt-nodejs’ package, so we need to install this package as well by running the command below:

$ npm install bcrypt-nodejs --save

And then, we are going to import the User model in our app.js, since we will use it later.

// import models
let User = require('./models/User');

Install the Passport package and the jsonwebtoken package

In the last section, we will install 3 packages which we will use later in this series.

The first one is passport.js which is a very popular authentication middleware for Node.js and it is very flexible and does the heavy-lifting for us when implementing an authentication system in a Node.js application. Let’s run the command below to install passport.js:

$ npm install passport --save

passport.js has a comprehensive set of strategies which support authentication using a username and password, Facebook, Twitter, and more.

In our application, we will use a username and password to authenticate and it is called ‘passport-local’ strategy. So the second package need to install is ‘passport-local’:

$ npm install passport-local --save

The third package we need to install is the ‘jsonwebtoken’ package which does a lot of weight-lifting work for us when using JSON Web Tokens in our application, such as sign a token and verify a token.

$ npm install jsonwebtoken --save

Ok, that is all for today. In the next tutorial in this series, we will work on the signup process.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

Authentication in React Apps with JWT: Part 2

This is the second part of our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. In this part, we will complete following tasks:

  • Set up Redux
  • Create pages
  • Set up React Router

Set up Redux

Redux is a predictable state container for Javascript app. In our React app, we will use it to track different states in our application. For example, it can tell us if there is a logged-in user or not.

First, we need to install ‘redux’ and ‘react-redux’ in our React app (in the ‘client’ directory).

$ npm install --save redux
npm install --save react-redux

Then, we need to import them to ‘client/src/index.js’ file.

import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';

Then, we will create reducers. Create a new file in ‘client/src/reducers.js’ and paste the code below to it:

export const cUser = (state, action) => {

    switch (action.type) {
        case 'SET_USER': 
            return Object.assign({}, action.data, {});
        default:
            return state || {};
    }

}

This reducer is used to update the ‘cUser’ state in our application which tells us if there is a logged-in user or not.

Then, we need to import it to the ‘client/src/index.js’ file:

import * as reducers from './reducers';

Then, in the ‘client/src/index.js’ file, we need to create our Redux store and make our React app respond to the change of Redux state, so that when a user logs in, the app will be re-rendered to show some related information or vice versa.

Let’s replace the code below:

ReactDOM.render(<App />, document.getElementById('root'));

with:

// Create the store
const store = createStore(combineReducers({
    cUser: reducers.cUser
}));

function run() {
    ReactDOM.render(<Provider store={store}>
        <App store={store} />
    </Provider>, document.getElementById('root'));
}

function init() {
    run();
    store.subscribe(run);
}

init();

Create pages

In this app, we need a signup page, a login page and a home page and all these pages will share the same top navigation bar. We are going to create a React component for each of these 3 pages and a component for the shared top navigation bar. All these 4 components are located in the ‘client/components’ directory. Please grab the code for these 4 components from the links below:

Set up React Router

In this section, we are going to use the new React Router 4 to link all those page components together in our app.

First, we need to install ‘react-router-dom’ which is the react router for web, ‘react-router-redux’ and ‘history’ which is used by ‘react-router-redux’. For more information, please refer to its official GitHub Repository.

$ npm install --save react-router-dom
$ npm install --save react-router-redux@next
$ npm install --save history

Then, we need to import them to the ‘client/src/index.js’ file:

import createHistory from 'history/createBrowserHistory';
import { ConnectedRouter, routerReducer, push } from 'react-router-redux';

Then, we need to create a history that our router will use in the ‘client/src/index.js’ file. Please make sure, this line of code is added before the run() function is defined, since we need to use it in the run() function.

// Create a history of your choosing (we're using a browser history in this case)
const history = createHistory();

Then, we update our run() function to:

function run() {
    ReactDOM.render(<Provider store={store}>
        <ConnectedRouter history={history}>
            <App store={store} />
        </ConnectedRouter>
    </Provider>, document.getElementById('root'));
}

Now, we need to go to the ‘client/src/App.js’ file to specify our routes and assemble all the components together. Please replace all existing code in file with the code below:

import React, { Component } from 'react';
import Navbar from './components/Navbar';
import { Route, Switch } from 'react-router-dom';
import HomePage from './components/HomePage';
import LoginPage from './components/LoginPage';
import SignupPage from './components/SignupPage';

class App extends Component {
    render() {
        return ( 
            <div className = "App" >
                <Navbar />
                <div className="container">
                    <Switch>
                        <Route path="/login" component={LoginPage} />
                        <Route path="/signup" component={SignupPage} />
                        <Route path="/" component={HomePage} />
                    </Switch>
                </div>
            </div>
        );
    }
}

export default App;

Great, we have completed all the tasks in the second tutorial of our series. In the next tutorial, we are going to do some work on the server side.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

×