This article is a continuation of Part 1 - User Registration in React. We recommened completing all parts in order.

TL;DR

Checkout the complete solution via GitHub. Don't forget though, you need a MeshyDB account to make this project run–don't panic, it's free!

Adding a Login Form

Component

To start we need to create a new component for logging in. This component will have a form and inputs for entering username and password. We’re going to come back to this page later, but for now we will leave the handleSubmit() function empty.

src/LoginForm.js (new)
import React from 'react'  
import { MeshyClient } from "@meshydb/sdk";  
import Navigation from './Navigation'; 

class LoginForm extends React.Component 
{ 
    constructor(props) 
    { 
        super(props);  

        this.state = 
        { 
            success: null, 
            message: '' 
        }; 

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }  

    handleChange(event)  
    { 
        const target = event.target; 
        const value = target.type === 'checkbox' ? target.checked : target.value; 
        const name = target.name; 

        this.setState({ [name]: value }); 

        return true; 
    } 

    handleSubmit(event)  
    { 
    } 

    render() 
    {  
        return (  
            <div> 
                <Navigation />
                <div className="row"> 
                    <div className="col-lg-8 offset-lg-2">  
                        {this.state.success === false && 
                        <p className="alert alert-danger" role="alert"> 
                            {this.state.message} 
                        </p>} 
                        {!this.state.success && 
                        <div> 
                            <form onSubmit={this.handleSubmit}> 
                                <h2 className="text-center">Log in</h2>        
                                <div className="form-group"> 
                                    <input type="text" className="form-control" placeholder="Username" name="username" required onChange={this.handleChange} />
                                </div> 
                                <div className="form-group"> 
                                    <input type="password" className="form-control" placeholder="Password" name="password" required onChange={this.handleChange} />
                                </div> 
                                <div className="form-group"> 
                                    <button type="submit" className="btn btn-primary btn-block">Log in</button> 
                                </div>         
                            </form> 
                        </div>}
                    </div>  
                </div> 
            </div>
        );  
    }  
 }  

export default LoginForm;

Routing

Now that we have a login form, we need to add this component to our app by adding an import statement to src/App.js.

src/App.js
import LoginForm from './LoginForm';

The next step is to add a login route. If the router detects “/login” in the url path it will render the <LoginForm> component.

src/App.js
<Route path="/login">  
    <LoginForm />
</Route>

Private Routes

In React, redirecting a user is done using the <Redirect> component included in the ‘react-router-dom' package. We will need to import this component by expanding the existing import statement in src/App.js.

src/App.js
import { 
  BrowserRouter as Router, 
  Switch, 
  Route, 
  Redirect 
} from "react-router-dom";

Higher Order Components

If you are building an app with a login page, chances are you want to restrict access to certain routes to only those people who are logged in. In this example, we are going to restrict access to the Home route if a user is not logged in.

To accomplish this, we are going to use a design pattern called Higher-Order Components (HOC). This pattern allows us to extend the existing <Route> component while adding new functionality. The functionality we are going to add is a simple check to see if the user is logged in. If the user is logged in we will render the component, otherwise we will render a <Redirect> component. The component will automaticaly redirect the user to the "/login" route.

To learn more about HOCs we suggest checking out React's documentation.

src/App.js
const PrivateRoute = ({ children, ...rest }) => { 
  return ( 
        <Route {...rest}
                render={({ location })=> 
                MeshyClient.currentConnection != null ? ( 
                  children 
                ) : ( 
            <Redirect to={{
              pathname: "/login",
              state: { from: location }
            }} />
        ) 
      } 
    /> 
  ); 
}; 

Now that we have our PrivateRoute HOC we can modify the existing “/home” route to use the <PrivateRoute> instead of <Route>. This will make sure the user is logged in before going to Home.

src/App.js
<PrivateRoute exact path="/">  
    <Home /> 
</PrivateRoute>

Navigation

By adding authentication, we’ve given our app two states, either the user is authenticated or unauthenticated. Depending on what state the user is in we want to show different navigation items. To do this we first need to add state to our <Navigation> component.

Authentication state is handled automatically via the MeshyClient.currentConnection property. To access this static member we will first need to import the MeshyClient package.

src/Navigation.js
import { MeshyClient } from "@meshydb/sdk"; 

Identifying if a user is authenticated is as simple as checking to see if MeshyClient.currentConnect is not null. We will store this variable in state so we can "react" to it—heh.

src/Navigation.js
constructor(props){
    super(props);

    this.state = { 
        authenticated: (MeshyClient.currentConnection != null) 
    }; 
}

Now that we have state, we can show the Register button if the user is not authenticated and show the Home button if the user is authenticated.

src/Navigation.js
{this.state.authenticated && 
<Nav.Link href="/">Home</Nav.Link>}
{!this.state.authenticated && 
<Nav.Link href="/register">Register</Nav.Link>} 

Authenticating the User

With all the necessary parts in place to protect a route and redirect to the login page, let's go back to our login form and add the final touch…. the OpenID authentication call.

What is OpenID?

OpenID is an open source standard for handling authentication and tokenized authorization. It is an extension of OAuth 2.0 in that it authenticates using the OAuth 2.0 protocol, however it also includes identity information in the form of an id_token.

By no means is this article intended to go into any level of detail on OpenID or OAuth 2.0, however it's important to know that MeshyDB was built using the OpenID protocol. To learn more about OpenID we suggest checking out the official website.

Abstracting Away Token Management

For the sake of this example, all you need to know about OpenID is that the MeshyClient takes care of the authentication and token management for you. Simply providing the username and password will allow you to login a user and have that user remain logged in until they signout.

src/LoginForm.js
handleSubmit(event)
{
    MeshyClient.login(this.state.username, this.state.password).then(_=> { 
          this.setState({ "success": true }); 
    }).catch(e=>{ 
          this.setState({ "message" : e.response.error_description }); 
          this.setState({ "success" : false }); 
    }); 

    event.preventDefault(); 
}

Redirecting Authorized Users

After the user logs in, you will want to redirect them to the home page. We are going to use the same <Redirect> component we used before to take care of this, but first we need to add the import statement.

src/LoginForm.js
import { Redirect } from "react-router-dom"; 

Next you will need to add a conditional <Redirect> component to the login form. The condition we are using is found in the component’s state variable and will be true only if the authentication was a success.

src/LoginForm.js
{this.state.success &&  
<Redirect to="/" />}

Adding Logout

The next step is creating a way for users to logout. Since everything in React is a component, we will begin by creating a new component to faciliate logout.

Component

This component will have state variable indicating if the logout was a success. The render method will conditionally use this state variable to either render a temporary <div> while waiting for logout to finish or a <Redirect> component once the logout succeeds.

When it comes to signing out, we want it to happen automatically when the component renders. The safest way to perform this is to use the componentDidMount() event handler. Be careful not to attempt sign out in the constructor on in the render function. Any attempt to modify state before the component mounts will result in an error. This function guarantees the component has mounted so we can safely update state.

src/Logout.js (new)
import React from 'react'  
import { MeshyClient } from "@meshydb/sdk";  
import { Redirect } from 'react-router-dom' 
  
class Logout extends React.Component 
{ 
    constructor(props) 
    { 
        super(props);  
  
        this.state = { 
            success: null 
        }; 
    } 
     
    componentDidMount() 
    { 
        if(MeshyClient.currentConnection != null) 
            MeshyClient.currentConnection.signout(); 
  
        this.setState({"success": true}); 
    } 
  
    render() 
    { 
        if(!this.state.success) 
            return (<div>signing out...</div>); 
        else 
            return (<Redirect to='/' />); 
    }  
 }  

 export default  Logout; 

Routing

Now that we have logout, we need to add this component to our app by adding an import statement to src/App.js.

src/App.js
import Logout from './Logout'; 

Once the component is imported, you can add a new route in src/App.js. In this example, we are going to have our logout page resolve with the “/logout” path.

src/App.js

Navigation

Finally, we need to add a new navigation item to our Navigation component. This item will show a logout button if the user is authenticated.

src/Navigation.js
{this.state.authenticated &&  
<Nav.Link href="/logout">Logout</Nav.Link>}

Putting it All Together

There you have it, a working user authentication form built using React and OpenID. And the best part is we didn't have to write any complex authentication or token management code!

We can verify our app works by logging in with the user we created from our previous article Part 1 - User Registration in React.

What Next?

This blog is the second part of a multi-part series where we will be demonstrating how to create a complete app using React. To continue in the series please click the link below. We suggest subscribing to our blog and following us on twitter for weekly updates.

Part 3 - Password Recovery in React