DEV Community

Cover image for Authentication With JWT (Json Web Token) In React - MERN Auth
CyberWolves
CyberWolves

Posted on

Authentication With JWT (Json Web Token) In React - MERN Auth

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

For better understanding watch Demo Video & Support

Source code of this project

Let's Start Coding...

Create React App & Install dependencies

$    npx create-react-app react-auth-jwt
$    cd react-auth-jwt
$    npm install react-router-dom axios @material-ui/core jwt-decode
$    npm start
Enter fullscreen mode Exit fullscreen mode

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

REACT_APP_API_URL = http://localhost:8080/api
Enter fullscreen mode Exit fullscreen mode

Configure Auth Service
/services/authServices.js

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");
}
Enter fullscreen mode Exit fullscreen mode

Configure User Service
/services/userServices.js

import axios from "axios";
const apiUrl = process.env.REACT_APP_API_URL;

export function register(data) {
    return axios.post(`${apiUrl}/users`, data);
}
Enter fullscreen mode Exit fullscreen mode

App.css

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;
}
Enter fullscreen mode Exit fullscreen mode

Home Component
/src/components/Home.jsx

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;
Enter fullscreen mode Exit fullscreen mode

Input Component
/src/components/common/Input.jsx

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;
Enter fullscreen mode Exit fullscreen mode

Form Component
/src/components/common/Form.js

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;
Enter fullscreen mode Exit fullscreen mode

Signup Component
/src/components/Signup.jsx

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;
Enter fullscreen mode Exit fullscreen mode

Login Component
/src/components/Login.jsx

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;
Enter fullscreen mode Exit fullscreen mode

Logout Component
/src/components/Logout.jsx

import { useEffect } from "react";
import { logout } from "../services/authServices";

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

export default Logout;
Enter fullscreen mode Exit fullscreen mode

React Router Dom
In Index.js

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")
);
Enter fullscreen mode Exit fullscreen mode

In App.js

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;
Enter fullscreen mode Exit fullscreen mode

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...

Top comments (0)