In this tutorial, we’re gonna build a React.js JWT Authentication: Login and Registration example with LocalStorage, React Router, Axios and Bootstrap (without Redux). I will show you:
- JWT Authentication Flow for User Signup & User Login
- Project Structure for React JWT Authentication (without Redux) with LocalStorage, React Router & Axios
- Creating React Components with Form Validation
- React Components for accessing protected Resources (Authorization)
- Dynamic Navigation Bar in React App
Related Posts:
– In-depth Introduction to JWT-JSON Web Token
– React.js CRUD example to consume Web API
– React File Upload with Axios and Progress Bar to Rest API
Fullstack (JWT Authentication & Authorization example):
– React + Spring Boot
– React + Node.js Express + MySQL/PostgreSQL
– React + Node.js Express + MongoDB
More Practice:
– React Hooks: JWT Authentication (without Redux) example
– Or using Redux for state management:
React Redux: JWT Authentication & Authorization example
– Typescript version: React Typescript JWT Authentication (without Redux) example
Contents
- Overview of React JWT Authentication example
- User Registration and User Login Flow
- React Component Diagram with Router, Axios & LocalStorage
- Technology
- Project Structure
- Setup React.js Project
- Add React Router
- Import Bootstrap
- Create Services
- Create React Components for Authentication
- Create React Components for accessing Resources
- Add Navbar and define Routes
- Logout when the Token is expired
- Add CSS style for React Components
- Configure Port for React JWT Auth Client with Web API
- Conclusion
- Further Reading
Overview of React JWT Authentication example
We will build a React application in that:
- There are Login/Logout, Signup pages.
- Form data will be validated by front-end before being sent to back-end.
- Depending on User’s roles (admin, moderator, user), Navigation Bar changes its items automatically.
Here are the screenshots:
– Signup Page:
– Form Validation Support:
– Login Page:
– Profile Page (for successful Login):
– For Moderator account login, the navigation bar will change by authorities:
– Check Browser Local Storage:
If you want to add refresh token, please visit:
React Refresh Token with JWT and Axios Interceptors
User Registration and User Login Flow
For JWT Authentication, we’re gonna call 2 endpoints:
- POST
api/auth/signup
for User Registration - POST
api/auth/signin
for User Login
The following flow shows you an overview of Requests and Responses that React Client will make or receive. This React Client must add a JWT to HTTP Header before sending request to protected resources.
You can find step by step to implement these back-end servers in following tutorial:
- Spring Boot JWT Authentication with Spring Security, MySQL
- Spring Boot JWT Authentication with Spring Security, PostgreSQL
- Spring Boot JWT Authentication with Spring Security, MongoDB
- Node.js JWT Authentication & Authorization with MySQL
- Node.js JWT Authentication & Authorization with MongoDB
- Node.js JWT Authentication & Authorization with PostgreSQL
Demo Video
This is full React + Node.js Express JWT Authentication & Authorization demo (with form validation, check signup username/email duplicates, test authorization with 3 roles: Admin, Moderator, User):
And this is using Spring Boot Server:
React Component Diagram with Router, Axios & LocalStorage
Let’s look at the diagram below.
– The App
component is a container with React Router (BrowserRouter
). Basing on the state, the navbar can display its items.
– Login
& Register
components have form for data submission (with support of react-validation
library). They call methods from auth.service
to make login/register request.
– auth.service
methods use axios
to make HTTP requests. Its also store or get JWT from Browser Local Storage inside these methods.
– Home
component is public for all visitor.
– Profile
component displays user information after the login action is successful.
– BoardUser
, BoardModerator
, BoardAdmin
components will be displayed by state user.roles
. In these components, we use user.service
to access protected resources from Web API.
– user.service
uses auth-header()
helper function to add JWT to HTTP header. auth-header()
returns an object containing the JWT of the currently logged in user from Local Storage.
Technology
We’re gonna use these modules:
- React 18/17
- react-router-dom 6.4.0
- axios 0.27.2
- react-validation 3.0.7
- Bootstrap 4
- validator 13.7.0
Project Structure
This is folders & files structure for this React application:
With the explanation in diagram above, you can understand the project structure easily.
Setup React.js Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-jwt-auth
Add React Router
– Run the command: npm install react-router-dom
.
– Open src/index.js and wrap App
component by BrowserRouter
object.
import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const container = document.getElementById("root");
const root = createRoot(container);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Import Bootstrap
Run command: npm install [email protected]
.
Open src/App.js and modify the code inside it as following-
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
class App extends Component {
render() {
// ...
}
}
export default App;
Create Services
We’re gonna create two services in src/services folder:
services
auth-header.js
auth.service.js (Authentication service)
user.service.js (Data service)
Before working with these services, we need to install Axios with command:
npm install axios
Authentication service
The service uses Axios for HTTP requests and Local Storage for user information & JWT.
It provides following important methods:
login()
: POST {username, password} & saveJWT
to Local Storagelogout()
: removeJWT
from Local Storageregister()
: POST {username, email, password}getCurrentUser()
: get stored user information (including JWT)
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
class AuthService {
login(username, password) {
return axios
.post(API_URL + "signin", {
username,
password
})
.then(response => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
}
logout() {
localStorage.removeItem("user");
}
register(username, email, password) {
return axios.post(API_URL + "signup", {
username,
email,
password
});
}
getCurrentUser() {
return JSON.parse(localStorage.getItem('user'));;
}
}
export default new AuthService();
Data service
We also have methods for retrieving data from server. In the case we access protected resources, the HTTP request needs Authorization header.
Let’s create a helper function called authHeader()
inside auth-header.js:
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return {};
}
}
The code above checks Local Storage for user
item. If there is a logged in user
with accessToken
(JWT), return HTTP Authorization header. Otherwise, return an empty object.
Note: For Node.js Express back-end, please use x-access-token header like this:
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// for Node.js Express back-end
return { 'x-access-token': user.accessToken };
} else {
return {};
}
}
Now we define a service for accessing data in user.service.js:
import axios from 'axios';
import authHeader from './auth-header';
const API_URL = 'http://localhost:8080/api/test/';
class UserService {
getPublicContent() {
return axios.get(API_URL + 'all');
}
getUserBoard() {
return axios.get(API_URL + 'user', { headers: authHeader() });
}
getModeratorBoard() {
return axios.get(API_URL + 'mod', { headers: authHeader() });
}
getAdminBoard() {
return axios.get(API_URL + 'admin', { headers: authHeader() });
}
}
export default new UserService();
You can see that we add a HTTP header with the help of authHeader()
function when requesting authorized resource.
Create React Components for Authentication
In src folder, create new folder named components and add several files as following:
components
login.component.js
register.component.js
profile.component.js
Form Validation overview
Now we need a library for Form validation, so we’re gonna add react-validation library to our project.
Run the command: npm install react-validation validator
To use react-validation in this example, you need to import following items:
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import { isEmail } from "validator";
We also use isEmail()
function from validator to verify email.
This is how we put them in render()
method with validations
attribute:
const required = value => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
const email = value => {
if (!isEmail(value)) {
return (
<div className="alert alert-danger" role="alert">
This is not a valid email.
</div>
);
}
};
render() {
return (
...
<Form
onSubmit={this.handleLogin}
ref={c => {this.form = c;}}
>
...
<Input
type="text"
className="form-control"
...
validations={[required, email]}
/>
<CheckButton
style={{ display: "none" }}
ref={c => {this.checkBtn = c;}}
/>
</Form>
...
);
}
We’re gonna call Form validateAll()
method to check validation functions in validations
. Then CheckButton
helps us to verify if the form validation is successful or not. So this button will not display on the form.
this.form.validateAll();
if (this.checkBtn.context._errors.length === 0) {
// do something when no error
}
This is another way to implement Form Validation:
React Form Validation example with Formik and Yup
Support Router functions
From the react-router-dom v6, the support for history
has been deprecated. So we need a wrapper (HOC) that can use new useful hooks.
In src folder, create common/with-router.js file with following code:
import { useLocation, useNavigate, useParams } from "react-router-dom";
export const withRouter = (Component) => {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return <Component {...props} router={{ location, navigate, params }} />;
}
return ComponentWithRouterProp;
};
Login Page
This page has a Form with username
& password
.
– We’re gonna verify them as required field.
– If the verification is ok, we call AuthService.login()
method, then direct user to Profile page: this.props.router.navigate("/profile");
, or show message
with response error.
login.component.js
import React, { Component } from "react";
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import AuthService from "../services/auth.service";
import { withRouter } from '../common/with-router';
const required = value => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
class Login extends Component {
constructor(props) {
super(props);
this.handleLogin = this.handleLogin.bind(this);
this.onChangeUsername = this.onChangeUsername.bind(this);
this.onChangePassword = this.onChangePassword.bind(this);
this.state = {
username: "",
password: "",
loading: false,
message: ""
};
}
onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
onChangePassword(e) {
this.setState({
password: e.target.value
});
}
handleLogin(e) {
e.preventDefault();
this.setState({
message: "",
loading: true
});
this.form.validateAll();
if (this.checkBtn.context._errors.length === 0) {
AuthService.login(this.state.username, this.state.password).then(
() => {
this.props.router.navigate("/profile");
window.location.reload();
},
error => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
this.setState({
loading: false,
message: resMessage
});
}
);
} else {
this.setState({
loading: false
});
}
}
render() {
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form
onSubmit={this.handleLogin}
ref={c => {
this.form = c;
}}
>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={this.state.username}
onChange={this.onChangeUsername}
validations={[required]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={this.state.password}
onChange={this.onChangePassword}
validations={[required]}
/>
</div>
<div className="form-group">
<button
className="btn btn-primary btn-block"
disabled={this.state.loading}
>
{this.state.loading && (
<span className="spinner-border spinner-border-sm"></span>
)}
<span>Login</span>
</button>
</div>
{this.state.message && (
<div className="form-group">
<div className="alert alert-danger" role="alert">
{this.state.message}
</div>
</div>
)}
<CheckButton
style={{ display: "none" }}
ref={c => {
this.checkBtn = c;
}}
/>
</Form>
</div>
</div>
);
}
}
export default withRouter(Login);
Register Page
This page is similar to Login Page.
For Form Validation, there are some more details:
username
: required, between 3 and 20 charactersemail
: required, email formatpassword
: required, between 6 and 40 characters
We’re gonna call AuthService.register()
method and show response message (successful or error).
register.component.js
import React, { Component } from "react";
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import { isEmail } from "validator";
import AuthService from "../services/auth.service";
const required = value => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
const email = value => {
if (!isEmail(value)) {
return (
<div className="alert alert-danger" role="alert">
This is not a valid email.
</div>
);
}
};
const vusername = value => {
if (value.length < 3 || value.length > 20) {
return (
<div className="alert alert-danger" role="alert">
The username must be between 3 and 20 characters.
</div>
);
}
};
const vpassword = value => {
if (value.length < 6 || value.length > 40) {
return (
<div className="alert alert-danger" role="alert">
The password must be between 6 and 40 characters.
</div>
);
}
};
export default class Register extends Component {
constructor(props) {
super(props);
this.handleRegister = this.handleRegister.bind(this);
this.onChangeUsername = this.onChangeUsername.bind(this);
this.onChangeEmail = this.onChangeEmail.bind(this);
this.onChangePassword = this.onChangePassword.bind(this);
this.state = {
username: "",
email: "",
password: "",
successful: false,
message: ""
};
}
onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
onChangeEmail(e) {
this.setState({
email: e.target.value
});
}
onChangePassword(e) {
this.setState({
password: e.target.value
});
}
handleRegister(e) {
e.preventDefault();
this.setState({
message: "",
successful: false
});
this.form.validateAll();
if (this.checkBtn.context._errors.length === 0) {
AuthService.register(
this.state.username,
this.state.email,
this.state.password
).then(
response => {
this.setState({
message: response.data.message,
successful: true
});
},
error => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
this.setState({
successful: false,
message: resMessage
});
}
);
}
}
render() {
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form
onSubmit={this.handleRegister}
ref={c => {
this.form = c;
}}
>
{!this.state.successful && (
<div>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={this.state.username}
onChange={this.onChangeUsername}
validations={[required, vusername]}
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<Input
type="text"
className="form-control"
name="email"
value={this.state.email}
onChange={this.onChangeEmail}
validations={[required, email]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={this.state.password}
onChange={this.onChangePassword}
validations={[required, vpassword]}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block">Sign Up</button>
</div>
</div>
)}
{this.state.message && (
<div className="form-group">
<div
className={
this.state.successful
? "alert alert-success"
: "alert alert-danger"
}
role="alert"
>
{this.state.message}
</div>
</div>
)}
<CheckButton
style={{ display: "none" }}
ref={c => {
this.checkBtn = c;
}}
/>
</Form>
</div>
</div>
);
}
}
Profile Page
This page gets current User from Local Storage by calling AuthService.getCurrentUser()
method and show user information (with token).
If the user is not logged in, redirect to /home
page.
profile.component.js
import React, { Component } from "react";
import { Navigate } from "react-router-dom";
import AuthService from "../services/auth.service";
export default class Profile extends Component {
constructor(props) {
super(props);
this.state = {
redirect: null,
userReady: false,
currentUser: { username: "" }
};
}
componentDidMount() {
const currentUser = AuthService.getCurrentUser();
if (!currentUser) this.setState({ redirect: "/home" });
this.setState({ currentUser: currentUser, userReady: true })
}
render() {
if (this.state.redirect) {
return <Navigate to={this.state.redirect} />
}
const { currentUser } = this.state;
return (
<div className="container">
{(this.state.userReady) ?
<div>
<header className="jumbotron">
<h3>
<strong>{currentUser.username}</strong> Profile
</h3>
</header>
<p>
<strong>Token:</strong>{" "}
{currentUser.accessToken.substring(0, 20)} ...{" "}
{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}
</p>
<p>
<strong>Id:</strong>{" "}
{currentUser.id}
</p>
<p>
<strong>Email:</strong>{" "}
{currentUser.email}
</p>
<strong>Authorities:</strong>
<ul>
{currentUser.roles &&
currentUser.roles.map((role, index) => <li key={index}>{role}</li>)}
</ul>
</div>: null}
</div>
);
}
}
Create React Components for accessing Resources
These components will use UserService
to request data from API.
components
home.component.js
board-user.component.js
board-moderator.component.js
board-admin.component.js
Home Page
This is a public page that shows public content. People don’t need to log in to view this page.
home.component.js
import React, { Component } from "react";
import UserService from "../services/user.service";
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
content: ""
};
}
componentDidMount() {
UserService.getPublicContent().then(
response => {
this.setState({
content: response.data
});
},
error => {
this.setState({
content:
(error.response && error.response.data) ||
error.message ||
error.toString()
});
}
);
}
render() {
return (
<div className="container">
<header className="jumbotron">
<h3>{this.state.content}</h3>
</header>
</div>
);
}
}
Role-based Pages
We’re gonna have 3 pages for accessing protected data:
- BoardUser page calls
UserService.getUserBoard()
- BoardModerator page calls
UserService.getModeratorBoard()
- BoardAdmin page calls
UserService.getAdminBoard()
I will show you User Page for example, other Pages are similar to this Page.
board-user.component.js
import React, { Component } from "react";
import UserService from "../services/user.service";
export default class BoardUser extends Component {
constructor(props) {
super(props);
this.state = {
content: ""
};
}
componentDidMount() {
UserService.getUserBoard().then(
response => {
this.setState({
content: response.data
});
},
error => {
this.setState({
content:
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
});
}
);
}
render() {
return (
<div className="container">
<header className="jumbotron">
<h3>{this.state.content}</h3>
</header>
</div>
);
}
}
You can simplify import statement with:
Absolute Import in React
Now we add a navigation bar in App
component. This is the root container for our application.
The navbar dynamically changes by login status and current User’s roles.
- Home: always
- Login & Sign Up: if user hasn’t signed in yet
- User:
AuthService.getCurrentUser()
returns a value - Board Moderator: roles includes
ROLE_MODERATOR
- Board Admin: roles includes
ROLE_ADMIN
src/App.js
import React, { Component } from "react";
import { Routes, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import AuthService from "./services/auth.service";
import Login from "./components/login.component";
import Register from "./components/register.component";
import Home from "./components/home.component";
import Profile from "./components/profile.component";
import BoardUser from "./components/board-user.component";
import BoardModerator from "./components/board-moderator.component";
import BoardAdmin from "./components/board-admin.component";
class App extends Component {
constructor(props) {
super(props);
this.logOut = this.logOut.bind(this);
this.state = {
showModeratorBoard: false,
showAdminBoard: false,
currentUser: undefined,
};
}
componentDidMount() {
const user = AuthService.getCurrentUser();
if (user) {
this.setState({
currentUser: user,
showModeratorBoard: user.roles.includes("ROLE_MODERATOR"),
showAdminBoard: user.roles.includes("ROLE_ADMIN"),
});
}
}
logOut() {
AuthService.logout();
this.setState({
showModeratorBoard: false,
showAdminBoard: false,
currentUser: undefined,
});
}
render() {
const { currentUser, showModeratorBoard, showAdminBoard } = this.state;
return (
<div>
<nav className="navbar navbar-expand navbar-dark bg-dark">
<Link to={"/"} className="navbar-brand">
bezKoder
</Link>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/home"} className="nav-link">
Home
</Link>
</li>
{showModeratorBoard && (
<li className="nav-item">
<Link to={"/mod"} className="nav-link">
Moderator Board
</Link>
</li>
)}
{showAdminBoard && (
<li className="nav-item">
<Link to={"/admin"} className="nav-link">
Admin Board
</Link>
</li>
)}
{currentUser && (
<li className="nav-item">
<Link to={"/user"} className="nav-link">
User
</Link>
</li>
)}
</div>
{currentUser ? (
<div className="navbar-nav ml-auto">
<li className="nav-item">
<Link to={"/profile"} className="nav-link">
{currentUser.username}
</Link>
</li>
<li className="nav-item">
<a href="/login" className="nav-link" onClick={this.logOut}>
LogOut
</a>
</li>
</div>
) : (
<div className="navbar-nav ml-auto">
<li className="nav-item">
<Link to={"/login"} className="nav-link">
Login
</Link>
</li>
<li className="nav-item">
<Link to={"/register"} className="nav-link">
Sign Up
</Link>
</li>
</div>
)}
</nav>
<div className="container mt-3">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/home" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/profile" element={<Profile />} />
<Route path="/user" element={<BoardUser />} />
<Route path="/mod" element={<BoardModerator />} />
<Route path="/admin" element={<BoardAdmin />} />
</Routes>
</div>
</div>
);
}
}
export default App;
Logout when the Token is expired
There are two ways. For more details, please visit:
React – How to Logout when JWT Token is expired
Add CSS style for React Components
Open src/App.css and write some CSS code as following:
label {
display: block;
margin-top: 10px;
}
.card-container.card {
max-width: 350px !important;
padding: 40px 40px;
}
.card {
background-color: #f7f7f7;
padding: 20px 25px 30px;
margin: 0 auto 25px;
margin-top: 50px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
}
.profile-img-card {
width: 96px;
height: 96px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
Configure Port for React JWT Auth Client with Web API
Because most of HTTP Server use CORS configuration that accepts resource sharing retrictted to some sites or ports, so we also need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
Now we’ve set our app running at port 8081
. You will need to do this work if you use one of following Servers:
- Spring Boot JWT Authentication with Spring Security MySQL
- Spring Boot JWT Authentication with Spring Security, PostgreSQL
- Spring Boot JWT Authentication with Spring Security, MongoDB
- Node.js JWT Authentication & Authorization with MySQL
- Node.js JWT Authentication & Authorization with MongoDB
- Node.js JWT Authentication & Authorization with PostgreSQL
Conclusion
Congratulation!
Today we’ve done so many interesting things. I hope you understand the overall layers of our React JWT Authentication App (without Redux) using LocalStorage, React Router, Axios, Bootstrap. Now you can apply it in your project at ease.
You should continue to check if Token is expired and logout:
React – How to Logout when JWT Token is expired
Or add refresh token:
React Refresh Token with JWT and Axios Interceptors
If you want to use React Hooks for this example, you can find the implementation at:
React Hooks: JWT Authentication (without Redux) example
Or using Redux for state management:
React Redux: JWT Authentication & Authorization example
Happy learning, see you again!
Further Reading
- React Router Guide
- React Components
- https://www.npmjs.com/package/react-validation
- https://www.npmjs.com/package/validator
- In-depth Introduction to JWT-JSON Web Token
Fullstack CRUD:
– React + Spring Boot + MySQL
– React + Spring Boot + PostgreSQL
– React + Spring Boot + MongoDB
– React + Node.js Express + MySQL
– React + Node.js Express + PostgreSQL
– React.js + Node.js Express + MongoDB
– React + Django
Another way to implement Form Validation:
React Form Validation example with Formik and Yup
Source Code
You can find the complete source code for this example on Github.
Typescript version:
React Typescript JWT Authentication (without Redux) example
Using HttpOnly Cookie for storing JWT:
React.js Login & Registration example – JWT & HttpOnly Cookie
Hi, Excelent tutorial.
This is helping me a lot :D.
I just need some help
When a user log in the user is still able to return to the /login page by typing it in the url. So my question is how to redirect the url to other page if the user has already logged in?
Sorry for my english.
Thanks for this. There are not many resources on this topic if you search on Google for something like “react user role authentication tutorial”. The one that ranks 1st, more than a tutorial, is a bunch of complicated code without explanation. Yours is easy to follow, with just the minimal requirements to make it work. Great for us just starting with React! I just wish I had found it sooner.
Hi bezkoder,
I got 404 when post: POSThttp://localhost:3000/api/auth/signin
Hi, if you use one of backend servers that I mentioned in the tutorial, you should notify the .env file where we configure port for api requests.
The port should be 8000 instead of 3000.
I have been in search of such a tutorial and this basically does it for me. This will save me a lot of headache and heartache, so thank you so much for this. At lease now I have a template to use with a python and Go backend.
How do you protect the routes such as /profile page on the backend?
Hi, you can read the tutorials for backend that I mentioned above.
Hello,
I tried to add some other routes and when i added [authJwt.verifyToken, authJwt.isModerator] to the routes in backend, but everyone even without log in could go to those routes. Do I need to do something extra to protect the routes?
Of course I also added const { authJwt } = require(“../middlewares”);
Hi
I’ve just tried to do that with middleware, But It’s not working
Can I have from you special help
Should we be saving the JWT to DB ?
This is great
Do you have same example with Functional component + React hook
I try to convert but keep getting errors.
Hi, here you are: React Hooks: JWT Authentication (without Redux) example
Friends, I need to make the following changes to this code. If I log in as an admin I enable access to all pages in the navigation bar. If I log in as a moderator I enable access to public and moderator pages. If I log in as a user, I can only access public pages. And where do I modify the code to load my pages instead of messages admin content, moderator content and user content?
Thank you for your help!
Hi Bezkoder,
Thank you for this nice explanation of how this works ! I felt that implementing Redux was a bit too messy and happily found out your implementation to help me out.
My bit of feedback would be to leave the validation part out of this tutorial and make a separate one for it as it makes it for much more code and is not at the essence of what you are trying to demonstrate (or maybe I am wrong) when reading the title.
Very nice tutorial! Thank you man!!!
Hello. I implemented this in my project (I used your tutorial on doing spring boot backend with PostgreSQL), but when I launch my localhost, I get “Unauthorized error: Full authentication is required to access this resource”. What I could’ve done wrong? Thank you.
Hi, you should check if http header is correct or not (Bearer for Spring Server and x-access-token for Node.js server)
Are the routes actually protected? The links are protected but what about the url path? Directly accessing the route would load the component? Or I am wrong?
Hi, you can protect the routes by checking the token every time user accessed the url. In the tutorial, I show you the way to protect on backend side. The component is still loaded, but protected data was not returned.
would you check the expiration of the token after decoding with `exp` and compare against current time to protect private component loading this way seems to avoid calling the server for checking if the token is invalid. Or would you check the token itself with the server for this ensure the signature is also valid on top of this? Maybe restricting component load based on exp is enough because you check the token on every API request anyways
Can you explain how can we set `Authorization` header for every request, if it exists in the `localStorage`.
Adding it to every request is feel like not a good practise.
Hi,
Nice tutorial, I got a question. How do you manage react pages with different layouts with private routes that can redirect?
Hi.
Thanks, you know how to teach beginners.
Great Tutorial! But I do have a stupid question: Where do I have to put the validation-code part into? I am speaking of the code after: npm install react-validation validator.
Thank you so much in advance!
Greetz,
H
Hey,Thankyou for this amazing tutorial.
How can i add a image logo in the navbar.I am a beginner and cannot figure it out.Any help will be appreciated.
Nice tutorial! If you don’t mind please provide tutorial something like this in nextjs too =) Thankss
Thank you! This tutorial is very helpful.
Hi, I just wanted to say thank you for this tutorial. It is so easy to follow and works like a charm. This has been very beneficial for me learning react authentication, thanks again.
Hi,
I like your tutorial style, and this is the second one I’m trying to rebuild. The first one didn’t get anything on screen (different browsers), but seem to work on the database-level, so I tried this one.
Unfortunattely still no luck. I get an error message “Module not found: Can’t resolve ‘react-validation/build/button’. I tried to solve this by manually installing ‘react-validation’ {npm install react-validation}.
But still no luck. Any suggestion ?
Thanks in advance
Hi,
Great tutorial, just like all the tutorials you post. I just have one question that might be a bug. If you log out and then use the back button of your browser, it will take you to the previous page and it doesn’t check for authentication. So it shows you the page. If you then refresh that page it checks for authentication and will divert to the login screen.
Am i missing something or did I do something wrong? You shouldnt be able to use the back button when logged out to see the previous page.
Thanks
Eddie
Hey,
Very nice tutorial!
I have a question regarding the logout process. How to implement a redirect/re-render here? So when the user is removed from the storage we should redirect to / or /login and rerender the page.
Many thanks,
TZ
Thanks again, everything works fine.
Greetings from Chile
hi, first thank you for this amazing work, i have runed both the back and front and but i don’t know how to link between them ??
Hi, you only need to run both with instruction in the tutorials. 🙂
– Backend: port 8080
– Frontend: port 8081
Then open browser with url:
http://localhost:8081/
Hi,
for me sign in is successful but when i Login again with same user name and password it says
“profile.component.js:20 Uncaught TypeError: Cannot read property ‘username’ of null”
and also it is not reading the below code:
{this.state.content}in home.component.js
so i changed the above code to
const {content} = this.props;
return (
{content}
now no error but home screen is blank without any content.
Please help me in the above 2 problems.
Hello,
I struggled with the same problem at the same line of code, I couldn’t figure it out. I realized that the accessToken was something different (token) for me. I would check the results you get from running the POST request in Postman and making sure the
if(response.data.(yourtokenhere))
in auth.service
and the
if(user&&user.(yourtokenhere))
in auth-header
have the same name of the token in it that you got in Postman. I hope this helps
error while signup
POST http://localhost:8080/api/auth/signup 500 (Internal Server Error)
TypeError: Cannot read property ‘username’ of undefined
at checkDuplicateUsernameOrEmail (/home/tsuraj/Videos/CovidCare/middlewares/verifySignUp.js:8:24)
Hi Suraj, I had the same problem and I found one solution :
In profile component , try to declare currentUser in your state like this :
this.state = {
currentUser: [AuthService.getCurrentUser()]
};
Because is an array of information about user.
In render , you can call this array like this, example for get username :
{this.state.currentUser[0].username}
Hi, if you get the error “TypeError: Cannot read property ‘username’ of undefined”, your version of express is different. This error means that you are not able to extract the body of the request.
So, you have to use bodyParser ( for pulling information from HTML POST (express4)).
Also if you are on the localhost, you have to use cors, in order to avoid browser cors error.
Hi,
First, amazing tutorials! Thank you so much!
I followed both the backend node js + mongodb JWT tutorial and the React one. Everything works perfectly except one thing. When i try to tunnel through NGROK, i get network error on the home page. Any idea why? Or how i can fix this?
Below is what is in the console log.
from origin ‘http://ba3f2c631ae9.ngrok.io’ has been blocked by CORS policy: The ‘Access-Control-Allow-Origin’ header has a value ‘http://localhost:8081’ that is not equal to the supplied origin.
Thank you very much for this, you’ve done an amazing work. It’s very understandable, and works perfectly.
Is it possible to use this as a starting point for my projects ?
Hi, thank you for your tutorials, it’s really helpful to me. Could you please tell me how to connect this react framework to your node.js backend? BTW, when I npm start this app, it returns Network Error. How this happens?
Hi, first you should look at how we set the HTTP request header in auth-header.js file.
Then, just run Node.js backend with the instruction in the tutorial.
There are some things you need to notice: CORS configuration for backend, and setting port in .env for frontend.
Hi there,
just a question, how do you set role on signup? I’m askin’ about it cuz I’m trying to define my role during the signUp but i can’t find when you send the role.. it seems always be a req with no role.. the code exit with a default role [1] = user
Hi, in this tutorial, the payload in signup request doesn’t have
role
and the backend processes'user'
as default role. If you wanna define role during the signup, you can addrole[]
inside the payload 🙂Hi again,
I have another question. It seems that the session expiration is only managed on server side but it should be on the front end side as well. Am I wrong? What can be done to do this?
Hi, you can use
sessionStorage
instead oflocalStorage
🙂I don’t think this is the solution according to this:
https://stackoverflow.com/questions/5523140/html5-local-storage-vs-session-storage
the session storage doesn’t manage automatically expiration. sessionStorage only saves by Tab instead of the whole browser so the benefit is minimal. I would prefer to keep logged in if I access from another tab.
The solution I am going to implement saves the timestamp together with the token in AuthService.login. AuthService.logout removes both the info. Whenever the application asks for the user token a check on expiration is done (the elapsed can be hardcoded but a better approach is to have it configurable). If the logged user is expired (passed the 1 day of validity) the Auth. getCurrentUser remove the user and timestamp from localStorage and returns null and the application understands the user must log out (go to the login page and refresh the window).
At this point, I have a question: if a token on server-side expires is it a good decision to log out a user and ask to login again? Wouldn’t a refresh token be better? I am new to SpringBoot and I do not know what are the best practices for Access Token vs Refresh Token.
Finally, there is then another typical scenario that Login scenarios must cover, log out a user after X minutes of inactivity. Any suggestions on how this can be done?
Thank you in advance for the help.
Ah yeah, now I understand your idea. You can research more details about Sliding-sessions.
I will try to implement and write the tutorial when having time 🙂
How do u store the id ,name , role and jwt token in the same time , my controller return just the token how can i do ?
Hi, you can see the way my controller returns HTTP response in the backend tutorial (Spring Boot/ Node.js Express). It contains all of id, name, role and jwt token.
i m using Asp.net core
how to use the moderator and admin login man
Hi, you need to use HTTP client to send signup request to backend server 🙂
Hi,
First of all, let me thank you for your great tutorial they saved me lot of time.
I just implemented the SpringBoot tutorial and it worked perfectly for my needs.
https://bezkoder.com/spring-boot-jwt-authentication/
Now I need to add a ReactJS front end and I am trying to add this tutorial. My problem is that when I submit login form I got:
Unhandled Rejection (TypeError): Cannot read property ‘push’ of undefined
at this line:
this.props.history.push(“/profile”);
consider that your code works and I am trying to import in my projects only parts I am interested in. The problem is that in this porting I don’t know what is causing the issue. Consider also that I am using the same libraries.
I solved adding this in the App.js:
“`
“`
then in the SignIn.js file where I have defined my SignIn component passed the history:
“`
“`
in your case, you don’t have a separate component for the form and this confused me.
Hi, I don’t know why your code didn’t display. You can post your code in raw format, I will modify it for better view 🙂
Hi,
Try again to put code. I think the problem are < and >. I have three files: App.js, SigniIn.js and SignInForm.js, basically a separate component for the form.
I solved adding this in the App.js:
before I had:
and in SignIn.js:
finally in SignIn for I could use:
Hope this time the code will be print
I did a better fix using withRouter from react-route-dom, basically, you only need to mark the components that need to use history. In my case, I avoid passing history from SignIn.js page to SignInForm and simply declare at the end of SignInForm.js this:
the problem is that in my app I have a sidebar and a status bar to update depending on whether the user is logged in or not. On both these components, I need to pass this.props.history including some intermediary components. The approach above simplifies everything.
In your case is not required but if people have some intermediary components not referenced directly from Route this should be the good approach to solve the problem.
Where exactly we should add this line :
?
Thank you for your effort to share the solution 🙂
Hello – your node, express, jwt, mongo, and react tutorials work great locally. However, when I push the app to Heroku, I get a network error when I try to login or create an account. Any suggestions?
Hi, please take a look at your CORS configuration, you need to change the origin 🙂
The problem is I am not sure what to change it to. Heroku dynamically sets the port. So does that mean I need to put the heroku app url in the cors setup? Also do I keep the auth-service and user-service ports for server?
Excellent tutorial, best one I’ve seen in fact! I cloned it from your site but when it comes up on localhost:8081, it shows “Network Error” in the content area uner the navbar. The NavBar looks correct and I if I click Sign Up link, it shows up. But if I enter a Username, Email, and Password, it also shows “Network Error” when I click the form’s blue Sign Up button. Suggestions?
Hi, this is just a front-end project. You should implement one of the backend servers that I provided in this tutorial for fullstack development 🙂
hey man you made same mistake ,you just have to download another source code for api of your choice and the run that api and the react npm start.
Same issue here
Hi, this React project is just a client, you need backend server for providing REST APIs. Please look at the tutorials I mention above.
Nice post. I just don’t like that `window.location.reload()` though 🙂
Thank You. It helped a lotz!!
This is a great tutorial. Intuitive and well explained. Do you have any examples of how to convert the localStoarge to an httpOnly cookie for the JWT token (read that Local Storage is easier to attack)
Hi, I will write a Tutorial for React JWT using cookies when having time 🙂
Awesome! Thank you and keep the tutorials coming.
Thank you so Much for this tutorial.
it s very helpfull.
Can i Modify your tutorial so that it can could use the new born “redux-toolkit”.
and How can i send it to .
let me know. Thank again for this tutorial
Hi, I’ve just written a tutorial using Redux for Authentication:
React Redux: Token Authentication example with JWT & Axios
Hello, great tutorial and very well explained.
1 question, do you recommend redux for this kind of projects (with many types of users and much more data from database) ?
Hi, I’ve just written a tutorial using Redux for Authentication:
React Redux: Token Authentication example with JWT & Axios
thanks for share this
by the way, in your code, my intelliJ also vscode have error for spread operator, any workaround to fix this.
Just clean the project and rebuild it 🙂
thanks
Hi Mr Bezkoder,
Thank you for this great tutorial!
You saved my day.
Could we have a Front-end version with React Redux and JWT Passport?
In any case thank you!!!
Hi, I will write the tutorial when having time 🙂
Sir, to me you are God sent , you have taught me what I have been trying and searching the whole web to learn I appreciate you a lot keep the good work.
Hi,
when I fetch GET method, everything is working, i.e.:
but for POST method, i.e.:
the result is: Access denied.
At first glance, do you see any error in this method?
Hi bezkoder, problem solved 🙂
For people interested in it:
Hi bezkoder,
I’m writing my application and your tutorial is very helpful for me. Everything is working well, including JWT etc. I can get all my data and post something in Postman using token.
But when I try to use POST method in fetch function, I receive “Access denied” message.
Could you show, please, how to write example fetch (or axios) method according to this tutorial? (with token use)
Hi, you can see the way we use token in Data_service section. 🙂
@bezkoder
Your tutorials are just awesome. Very helpful
Hi, thank you very much, you are the best!
Could you please so kind to share source code?
Hi, you can follow the steps in the tutorial and try to make your project work.
I’ve tested the source code and it worked definitely. But I think sharing the source code early will not help people because they need to try their best first.
Yeah, I will upload my code to Github. 🙂
You can return to this page and get it in 2 or 3 weeks.
you are great man!! people who tried this code will always thank you for a great start.
Excellent post, it helped me a lot. Thank you!.
Could you give me an idea of how to do this with Hooks (useState, useEffect)?
Hi, I will write the tutorial for React CRUD using Hooks in some days 🙂
Thank you for all your effort.
How can I choose the roles is either user or admin or moderator . It’s makes me confuse . Please help me out
too good article
Excellent work!!! But my question is, what if I don’t want to use BoardAdmin, BoardModerator, BoardUser in the navbar link. I have my links already just to create a login & register Page. Please I need your help on this working on my bootcamp project