Authentication With JWT (Json Web Token) In React - MERN Auth
Tags: react, javascript, webdev
Published: 2021-06-09 16:59:28

Hi guys, Today we gonna implement authentication in React. We gonna build beautiful forms and handle APIs. Back-end of this project is already built, download it from below post.

[Node.js API Authentication with JWT (Json Web Token) - Auth Middleware](https://dev.to/jahangeer/node-js-api-authentication-with-jwt-json-web-token-auth-middleware-ggm)

For better understanding watch **Demo Video & Support**
{% youtube GN-ptFhBTig %}
[Source code of this project](https://github.com/sk-Jahangeer/react-auth-jwt)

Let's Start Coding...

**Create React App & Install dependencies**
```javascript
$    npx create-react-app react-auth-jwt
$    cd react-auth-jwt
$    npm install react-router-dom axios @material-ui/core jwt-decode
$    npm start
```
**react-router-dom :** A tool that allows you to handle routes in a web app
**axios :** It's a promise based HTTP client for the browser and Node.js
**@material-ui/core (optional) :** Makes writing beautiful UI easy
**jwt-decode :** It helps decoding JWT token

**Configure Environmental Variables**
/.env
```env
REACT_APP_API_URL = http://localhost:8080/api
```
**Configure Auth Service**
/services/authServices.js
```javascript
import axios from "axios";
import jwtDecode from "jwt-decode";
const apiUrl = process.env.REACT_APP_API_URL;

export function login(data) {
    return axios.post(`${apiUrl}/auth`, data);
}

export function getCurrentUser() {
    try {
        const token = localStorage.getItem("token");
        return jwtDecode(token);
    } catch (error) {
        return null;
    }
}

export function logout() {
    localStorage.removeItem("token");
}
```
**Configure User Service**
/services/userServices.js
```javascript
import axios from "axios";
const apiUrl = process.env.REACT_APP_API_URL;

export function register(data) {
    return axios.post(`${apiUrl}/users`, data);
}
```
**App.css**
```javascript
a {
    text-decoration: none;
}

.flex {
    display: flex;
    justify-content: center;
    align-items: center;
}

.column {
    flex-direction: column;
}

.full_screen {
    width: 100vw;
    height: 100vh;
}

.form {
    display: flex;
    flex-direction: column;
    width: 300px;
    padding: 20px;
}

.form_heading {
    font-size: 25px;
    font-weight: bold;
    text-align: center;
    margin-bottom: 15px;
}

.input {
    width: 100% !important;
    margin: 5px 0 !important;
}
```
**Home Component**
/src/components/Home.jsx
```javascript
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { getCurrentUser } from "../services/authServices";
import { AppBar, Toolbar, Button } from "@material-ui/core";

const Home = () => {
    const [user, setUser] = useState("");

    useEffect(() => {
        setUser(getCurrentUser());
    }, []);

    return (
        <AppBar color="default">
            <Toolbar>
                <h3 style={{ flexGrow: "1" }}>Domain</h3>
                {!user && (
                    <React.Fragment>
                        <Link to="/login">
                            <Button
                                style={{ marginRight: "10px" }}
                                variant="outlined"
                                color="secondary"
                            >
                                Login
                            </Button>
                        </Link>
                        <Link to="/signup">
                            <Button variant="outlined" color="secondary">
                                Signup
                            </Button>
                        </Link>
                    </React.Fragment>
                )}
                {user && (
                    <React.Fragment>
                        <h4 style={{ marginRight: "15px" }}>{user.name}</h4>
                        <Link to="/logout">
                            <Button variant="outlined" color="secondary">
                                Logout
                            </Button>
                        </Link>
                    </React.Fragment>
                )}
            </Toolbar>
        </AppBar>
    );
};

export default Home;
```
**Input Component**
/src/components/common/Input.jsx
```javascript
import React from "react";
import { TextField } from "@material-ui/core";

const Input = ({ error, ...rest }) => {
    return (
        <React.Fragment>
            {error ? (
                <TextField
                    {...rest}
                    error
                    helperText={error}
                    size="small"
                    variant="outlined"
                    className="input"
                />
            ) : (
                <TextField
                    {...rest}
                    variant="outlined"
                    size="small"
                    className="input"
                />
            )}
        </React.Fragment>
    );
};

export default Input;
```
**Form Component**
/src/components/common/Form.js
```javascript
import React, { Component } from "react";
import { Button } from "@material-ui/core";
import Input from "./Input";

class Form extends Component {
    state = { data: {}, errors: {} };

    handleChange = ({ currentTarget: input }) => {
        const data = { ...this.state.data };
        data[input.name] = input.value;
        this.setState({ data });
    };

    handleSubmit = (event) => {
        event.preventDefault();
        this.doSubmit();
    };

    renderInput(name, label, type = "text", required = true) {
        const { data, errors } = this.state;
        return (
            <Input
                name={name}
                label={label}
                type={type}
                required={required}
                value={data[name]}
                error={errors[name]}
                onChange={this.handleChange}
            />
        );
    }

    renderSubmitBtn(name) {
        return (
            <Button
                type="submit"
                style={{ marginLeft: "auto" }}
                variant="outlined"
                size="medium"
                color="secondary"
            >
                {name}
            </Button>
        );
    }
}

export default Form;
```
**Signup Component**
/src/components/Signup.jsx
```javascript
import React from "react";
import { Link } from "react-router-dom";
import { register } from "../services/userServices";
import { Paper } from "@material-ui/core";
import Form from "./common/Form";

class Signup extends Form {
    state = { data: { name: "", email: "", password: "" }, errors: {} };

    doSubmit = async () => {
        try {
            await register(this.state.data);
            this.props.history.push("/login");
        } catch (error) {
            console.log(error);
        }
    };

    render() {
        return (
            <form
                onSubmit={this.handleSubmit}
                className="full_screen flex column"
            >
                <Paper elevation={3} className="form">
                    <div className="form_heading">Signup</div>
                    {this.renderInput("name", "Name")}
                    {this.renderInput("email", "Email", "email")}
                    {this.renderInput("password", "Password", "password")}
                    {this.renderSubmitBtn("Signup")}
                </Paper>
                <div style={{ margin: "10px 0" }}>
                    Already have an account? <Link to="/login">Login</Link>
                </div>
            </form>
        );
    }
}

export default Signup;
```
**Login Component**
/src/components/Login.jsx
```javascript
import React from "react";
import { Link } from "react-router-dom";
import { login } from "../services/authServices";
import { Paper } from "@material-ui/core";
import Form from "./common/Form";

class Login extends Form {
    state = { data: { email: "", password: "" }, errors: {} };

    doSubmit = async () => {
        try {
            const { data } = await login(this.state.data);
            window.localStorage.setItem("token", data);
            window.location = "/";
        } catch (error) {
            const errors = { ...this.state.errors };
            errors.email = error.response.data;
            errors.password = error.response.data;
            this.setState({ errors });
        }
    };

    render() {
        return (
            <form
                onSubmit={this.handleSubmit}
                className="full_screen flex column"
            >
                <Paper className="form" elevation={3}>
                    <div className="form_heading">Login</div>
                    {this.renderInput("email", "Email", "email")}
                    {this.renderInput("password", "Password", "password")}
                    {this.renderSubmitBtn("Login")}
                </Paper>
                <div style={{ margin: "10px 0" }}>
                    Don't have an account? <Link to="/signup">Signup</Link>
                </div>
            </form>
        );
    }
}

export default Login;
```
**Logout Component**
/src/components/Logout.jsx
```javascript
import { useEffect } from "react";
import { logout } from "../services/authServices";

const Logout = () => {
    useEffect(() => {
        logout();
        window.location = "/";
    }, []);
    return null;
};

export default Logout;
```
**React Router Dom**
In **Index.js**
```javascript
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import "./index.css";
import App from "./App";

ReactDOM.render(
    <React.StrictMode>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </React.StrictMode>,
    document.getElementById("root")
);
```
In **App.js**
```javascript
import {Switch, Route} from 'react-router-dom';
import Home from './components/Home';
import Signup from './components/Signup';
import Login from './components/Login';
import Logout from './components/Logout';
import "./App.css";

function App() {
    return (
        <Switch>
            <Route path="/" exact component={Home} />
            <Route path="/signup" component={Signup} />
            <Route path="/login" component={Login} />
            <Route path="/logout" component={Logout} />
        </Switch>
    );
}

export default App;
```

That's it Run on local server and test app. If you found any mistakes or making code better please let me know in comment. I hope you have learned something.

**Thank you...**