├── .gitignore
├── README.md
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.css
├── App.js
├── App.test.js
├── app
│ ├── components
│ │ ├── AdminPage.js
│ │ ├── AppNavbar.js
│ │ ├── Home.js
│ │ ├── Login.js
│ │ ├── Profile.js
│ │ ├── ProjectManagerPage.js
│ │ ├── SignUp.js
│ │ └── UserPage.js
│ └── services
│ │ ├── AuthenticationService.js
│ │ └── BackendService.js
├── avatar.png
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | https://loizenai.com/reactjs-jwt-authentication-example/
2 |
3 | # Reactjs JWT Authentication Example
4 |
5 | Tutorial: Reactjs JWT Token Authentication Example
6 |
7 |
8 |
9 | JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. So in the tutorial, I introduce how to implement an application “Reactjs JWT token Authentication Example” with details step by step and 100% running sourcecode.
10 |
11 | – I give you an Epic of the application, a fullstack excutive flow from frontend to backend with overall architecture diagram.
12 | – I give you a layer diagram of Reactjs JWT Application.
13 | – I give you an implementatin of security backend sourcecode (SpringBoot + Nodejs JWT RestAPIs).
14 | – I guide you step by step how to develop a Reactjs JWT Authentication application.
15 | – Finally, I do an integrative testing from Reactjs JWT Authentication application to Backend Security RestAPIs
16 |
17 | 
18 |
19 | ## Overall Epic System Architecture Diagram
20 |
21 | 
22 |
23 | For the Reactjs JWT Authentication tutorial, we have 2 projects:
24 | – Backend project (using SpringBoot or Nodejs Express) provides secured RestAPIs with JWT token.
25 | – Reactjs project will request RestAPIs from Backend system with the JWT Token Authentication implementation.
26 |
27 | ## JWT Authentication Sequence Diagram
28 |
29 | The diagram below show how our system handles User Registration and User Login processes:
30 |
31 | 
32 |
33 | 1. User Registration Phase:
34 | – User uses a React.js register form to post user’s info (name, username, email, role, password) to Backend API /api/auth/signup.
35 | – Backend will check the existing users in database and save user’s signup info to database. Finally, It will return a message (successfully or fail) to
36 |
37 | 2. User Login Phase:
38 | – User posts user/password to signin to Backend RestAPI /api/auth/signin.
39 | – Backend will check the username/password, if it is right, Backend will create and JWT string with secret then return it to Reactjs client.
40 |
41 | After signin, user can request secured resources from backend server by adding the JWT token in Authorization Header. For each request, backend will check the JWT signature and then returns back the resources based on user’s registered authorities.
42 |
43 | ## Reactjs JWT Authentication Layer Diagram Overview
44 |
45 | 
46 |
47 | Reactjs JWT Authentication would be built with 5 main kind blocks:
48 |
49 | - Reactjs Router is a standard library for routing in React. It enables the navigation among views of various components in a React Application, allows changing the browser URL, and keeps the UI in sync with the URL.
50 | - Reactjs Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
51 | - Reactjs Service is a bridge between Reactjs Component and Backend Server, it is used to do technical logic with Backend Server (using Ajax Engine to fetch data from Backend, or using Local Storage to save user login data) and returned a response data to React.js Components
52 | - Local Storage allow to save key/value pairs in a web browser. It is a place to save the login user’s info.
53 | - Axios – (an Ajax Engine) is a promise-based HTTP client for the browser and Node. js. Axios makes it easy to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.
54 |
55 | ## Project Goal
56 |
57 | We create a Reactjs JWT Authentication project as below:
58 |
59 | 
60 |
61 | It includes 8 components and 2 services and a router in app.js file.
62 |
63 | – Home page:
64 |
65 | 
66 |
67 | – User Register page:
68 |
69 | 
70 |
71 | – Login Page:
72 |
73 | 
74 |
75 | – Profile Page:
76 |
77 | 
78 |
79 | – Use Page:
80 |
81 | 
82 |
83 | – Project Manager Page:
84 |
85 | 
86 |
87 | – Reactjs Admin page:
88 |
89 | 
90 |
91 | ## Related posts
92 |
93 | [How to Integrate Reactjs with Nodejs Tutorial](https://loizenai.com/integrate-reactjs-nodejs-tutorial/)
94 | [Tutorial: SpringBoot + React + MongoDB – SpringBoot React.js CRUD Example](https://loizenai.com/springboot-reactjs-mongodb-crud/)
95 | [Angular 10 + Nodejs JWT Token Based Authentication with MySQL Example – Express RestAPIs + JWT + BCryptjs + Sequelize](https://loizenai.com/angular-10-nodejs-jwt-authentication-mysql-examples-tutorials/)
96 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactjs-jwt-authentication",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "axios": "^0.21.0",
10 | "bootstrap": "^4.5.3",
11 | "react": "^17.0.1",
12 | "react-bootstrap": "^1.4.0",
13 | "react-cookie": "3.0.4",
14 | "react-dom": "^17.0.1",
15 | "react-router-dom": "4.3.1",
16 | "react-scripts": "4.0.0",
17 | "reactstrap": "^8.7.1",
18 | "web-vitals": "^0.2.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "proxy": "http://localhost:8080",
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loizenai/reactjs-jwt-authentication/ed7fdb48c666b1dea85b0f2b1ae145b510d908a3/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loizenai/reactjs-jwt-authentication/ed7fdb48c666b1dea85b0f2b1ae145b510d908a3/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loizenai/reactjs-jwt-authentication/ed7fdb48c666b1dea85b0f2b1ae145b510d908a3/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .center {
2 | display: block;
3 | margin-left: auto;
4 | margin-right: auto;
5 | width: 50%;
6 | }
7 |
8 | .App {
9 | text-align: center;
10 | }
11 |
12 | .App-logo {
13 | height: 40vmin;
14 | pointer-events: none;
15 | }
16 |
17 | @media (prefers-reduced-motion: no-preference) {
18 | .App-logo {
19 | animation: App-logo-spin infinite 20s linear;
20 | }
21 | }
22 |
23 | .App-header {
24 | background-color: #282c34;
25 | min-height: 100vh;
26 | display: flex;
27 | flex-direction: column;
28 | align-items: center;
29 | justify-content: center;
30 | font-size: calc(10px + 2vmin);
31 | color: white;
32 | }
33 |
34 | .App-link {
35 | color: #61dafb;
36 | }
37 |
38 | @keyframes App-logo-spin {
39 | from {
40 | transform: rotate(0deg);
41 | }
42 | to {
43 | transform: rotate(360deg);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
3 | import './App.css';
4 | import Home from './app/components/Home';
5 | import Profile from './app/components/Profile';
6 | import UserPage from './app/components/UserPage';
7 | import ProjectManagerPage from './app/components/ProjectManagerPage';
8 | import SignUp from './app/components/SignUp';
9 | import AdminPage from './app/components/AdminPage';
10 | import Login from './app/components/Login';
11 |
12 | class App extends Component {
13 | render() {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 | }
30 |
31 | export default App;
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/app/components/AdminPage.js:
--------------------------------------------------------------------------------
1 | import AppNavbar from './AppNavbar';
2 | import React, { Component } from 'react';
3 | import { Container } from 'reactstrap';
4 | import { Alert } from "reactstrap";
5 | import BackendService from '../services/BackendService';
6 |
7 | class AdminPage extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state={
11 | content: "",
12 | error: ""
13 | }
14 | }
15 |
16 | componentDidMount() {
17 | BackendService.getAdminBoard()
18 | .then( response => {
19 | this.setState({
20 | content: response.data
21 | })
22 | } , error => {
23 | console.log(error);
24 | this.setState({
25 | error: error.toString()
26 | });
27 | });
28 | }
29 |
30 | render() {
31 | return (
32 |
33 |
34 |
35 | {
36 | this.state.content ? (
37 |
38 |
39 | {this.state.content}
40 |
41 |
42 | ) : (
43 |
44 |
45 | {this.state.error}
46 |
47 |
48 | )
49 | }
50 |
51 |
52 | );
53 | }
54 | }
55 |
56 | export default AdminPage;
--------------------------------------------------------------------------------
/src/app/components/AppNavbar.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavbarText, NavItem, NavLink } from 'reactstrap';
3 | import { Link } from 'react-router-dom';
4 |
5 | import { withRouter } from 'react-router-dom';
6 |
7 | import AuthenticationService from '../services/AuthenticationService';
8 |
9 | class AppNavbar extends Component {
10 | constructor(props) {
11 | super(props);
12 | this.state = {isOpen: false};
13 | this.toggle = this.toggle.bind(this);
14 |
15 | this.state = {
16 | showUser: false,
17 | showPM: false,
18 | showAdmin: false,
19 | username: undefined,
20 | login: false
21 | };
22 | }
23 |
24 | componentDidMount() {
25 | const user = AuthenticationService.getCurrentUser();
26 |
27 | if (user) {
28 | const roles = [];
29 |
30 | user.authorities.forEach(authority => {
31 | roles.push(authority.authority)
32 | });
33 |
34 | this.setState({
35 | showUser: true,
36 | showPM: roles.includes("ROLE_PM") || roles.includes("ROLE_ADMIN"),
37 | showAdmin: roles.includes("ROLE_ADMIN"),
38 | login: true,
39 | username: user.username
40 | });
41 | }
42 | }
43 |
44 | signOut = () => {
45 | AuthenticationService.signOut();
46 | this.props.history.push('/home');
47 | window.location.reload();
48 | }
49 |
50 | toggle() {
51 | this.setState({
52 | isOpen: !this.state.isOpen
53 | });
54 | }
55 |
56 | render() {
57 | return
58 | Loizenai.com
59 |
65 |
66 |
67 | {
68 | this.state.login ? (
69 |
79 | ) : (
80 |
88 | )
89 | }
90 |
91 | ;
92 | }
93 | }
94 |
95 | export default withRouter(AppNavbar);
--------------------------------------------------------------------------------
/src/app/components/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import AppNavbar from './AppNavbar';
3 | import { Link } from 'react-router-dom';
4 | import { Button, Container } from 'reactstrap';
5 |
6 | class Home extends Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | }
11 |
12 | componentDidMount() {
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | Reactjs JWT Authentication Application
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | export default Home;
--------------------------------------------------------------------------------
/src/app/components/Login.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import AppNavbar from './AppNavbar';
3 | import { Container } from 'reactstrap';
4 | import { Form, Alert, FormGroup, Input, Label, Row, Col } from "reactstrap";
5 | import {Button} from 'react-bootstrap';
6 | import AuthenticationService from "../services/AuthenticationService";
7 | import avatar from '../../avatar.png';
8 |
9 | import '../../App.css';
10 |
11 | class Login extends Component {
12 |
13 | constructor(props) {
14 | super(props);
15 |
16 | this.state = {
17 | username: "",
18 | password: "",
19 | error: ""
20 | };
21 | }
22 |
23 | changeHandler = (event) => {
24 | let nam = event.target.name;
25 | let val = event.target.value;
26 | this.setState({[nam]: val});
27 | }
28 |
29 | doLogin = async (event) => {
30 | event.preventDefault();
31 |
32 | AuthenticationService
33 | .signin(this.state.username,
34 | this.state.password)
35 | .then(
36 | () => {
37 | this.props.history.push('/profile');
38 | },
39 | error => {
40 | console.log("Login fail: error = { " + error.toString() + " }");
41 | this.setState({error: "Can not signin successfully ! Please check username/password again"});
42 | }
43 | );
44 | }
45 |
46 | render() {
47 | return (
48 |
49 |
50 |
51 |
52 |
53 |
54 |

56 |
57 |
92 |
93 |
94 |
95 |
);
96 | }
97 | }
98 |
99 | export default Login;
--------------------------------------------------------------------------------
/src/app/components/Profile.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import AppNavbar from './AppNavbar';
3 | import { Link } from 'react-router-dom';
4 | import { Button, Container } from 'reactstrap';
5 | import { Alert } from "react-bootstrap"
6 |
7 | import AuthenticationService from '../services/AuthenticationService';
8 |
9 | class Profile extends Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {user: undefined};
14 | }
15 |
16 | componentDidMount() {
17 | const user = AuthenticationService.getCurrentUser();
18 | this.setState({user: user});
19 | }
20 |
21 | render() {
22 | let userInfo = "";
23 | const user = this.state.user;
24 |
25 | // login
26 | if (user && user.accessToken) {
27 |
28 | let roles = "";
29 |
30 | user.authorities.forEach(authority => {
31 | roles = roles + " " + authority.authority
32 | });
33 |
34 | userInfo = (
35 |
36 |
37 | User Info
38 |
39 | - Username: {user.username}
40 | - Access Token: {user.accessToken}
41 | - Authorities: {roles}
42 |
43 |
44 |
45 | );
46 | } else { // not login
47 | userInfo =
48 |
49 | Profile Component
50 |
51 |
52 |
53 | }
54 |
55 | return (
56 |
57 |
58 |
59 | {userInfo}
60 |
61 |
62 | );
63 | }
64 | }
65 |
66 | export default Profile;
--------------------------------------------------------------------------------
/src/app/components/ProjectManagerPage.js:
--------------------------------------------------------------------------------
1 | import AppNavbar from './AppNavbar';
2 | import React, { Component } from 'react';
3 | import { Container } from 'reactstrap';
4 | import { Alert } from "reactstrap";
5 | import BackendService from '../services/BackendService';
6 |
7 | class ProjectManagerPage extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state={
11 | content: "",
12 | error: ""
13 | }
14 | }
15 |
16 | componentDidMount() {
17 | BackendService.getPmBoard()
18 | .then( response => {
19 | this.setState({
20 | content: response.data
21 | })
22 | } , error => {
23 | console.log(error);
24 | this.setState({
25 | error: error.toString()
26 | });
27 | });
28 | }
29 |
30 | render() {
31 | return (
32 |
33 |
34 |
35 | {
36 | this.state.content ? (
37 |
38 |
39 | {this.state.content}
40 |
41 |
42 | ) : (
43 |
44 |
45 | {this.state.error}
46 |
47 |
48 | )
49 | }
50 |
51 |
52 | );
53 | }
54 | }
55 |
56 | export default ProjectManagerPage;
--------------------------------------------------------------------------------
/src/app/components/SignUp.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import AppNavbar from './AppNavbar';
3 | import { Container } from 'reactstrap';
4 | import { Button, Form, FormGroup, Input, Label, Row, Col } from "reactstrap";
5 | import { Alert } from "react-bootstrap"
6 |
7 | import Authentication from '../services/AuthenticationService'
8 |
9 | const validEmailRegex = RegExp(/^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i);
10 |
11 | const validateForm = (errors) => {
12 | let valid = true;
13 | Object.values(errors).forEach(
14 | (val) => val.length > 0 && (valid = false)
15 | );
16 | return valid;
17 | }
18 |
19 | class SignUp extends Component {
20 |
21 | constructor(props) {
22 | super(props);
23 | this.state = {
24 | firstname: "",
25 | lastname: "",
26 | username: "",
27 | email: "",
28 | password: "",
29 | message: "",
30 | successful: false,
31 | validForm: true,
32 | errors: {
33 | firstname: '',
34 | lastname: '',
35 | username: '',
36 | email: '',
37 | password: ''
38 | }
39 | };
40 | }
41 |
42 | changeHandler = (event) => {
43 | const { name, value } = event.target;
44 |
45 | let errors = this.state.errors;
46 |
47 | switch (name) {
48 | case 'firstname':
49 | errors.firstname =
50 | value.length < 3
51 | ? 'FirstName must be 3 characters long!'
52 | : '';
53 | break;
54 | case 'lastname':
55 | errors.lastname =
56 | value.length < 3
57 | ? 'LastName must be 3 characters long!'
58 | : '';
59 | break;
60 | case 'username':
61 | errors.username =
62 | value.length < 5
63 | ? 'Username must be 5 characters long!'
64 | : '';
65 | break;
66 | case 'email':
67 | errors.email =
68 | validEmailRegex.test(value)
69 | ? ''
70 | : 'Email is not valid!';
71 | break;
72 | case 'password':
73 | errors.password =
74 | value.length < 8
75 | ? 'Password must be 8 characters long!'
76 | : '';
77 | break;
78 | default:
79 | break;
80 | }
81 |
82 | this.setState({errors, [name]: value}, ()=> {
83 | console.log(errors)
84 | })
85 | }
86 |
87 | signUp = (e) => {
88 | e.preventDefault();
89 | const valid = validateForm(this.state.errors);
90 | this.setState({validForm: valid});
91 | if(valid){
92 | Authentication.register(
93 | this.state.firstname,
94 | this.state.lastname,
95 | this.state.username,
96 | this.state.email,
97 | this.state.password
98 | ).then(
99 | response => {
100 | this.setState({
101 | message: response.data.message,
102 | successful: true
103 | });
104 | },
105 | error => {
106 | console.log("Fail! Error = " + error.toString());
107 |
108 | this.setState({
109 | successful: false,
110 | message: error.toString()
111 | });
112 | }
113 | );
114 | }
115 | }
116 |
117 | render() {
118 | const title = Register User
;
119 | const errors = this.state.errors;
120 |
121 | let alert = "";
122 |
123 | if(this.state.message){
124 | if(this.state.successful){
125 | alert = (
126 |
127 | {this.state.message}
128 |
129 | );
130 | }else{
131 | alert = (
132 |
133 | {this.state.message}
134 |
135 | );
136 | }
137 | }
138 |
139 | return (
140 |
141 |
142 |
143 |
144 |
145 | {title}
146 |
255 |
256 |
257 |
258 |
);
259 | }
260 | }
261 |
262 | export default SignUp;
--------------------------------------------------------------------------------
/src/app/components/UserPage.js:
--------------------------------------------------------------------------------
1 | import AppNavbar from './AppNavbar';
2 | import React, { Component } from 'react';
3 | import { Container } from 'reactstrap';
4 | import BackendService from '../services/BackendService';
5 | import { Alert } from "react-bootstrap"
6 |
7 | class UserPage extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state={
11 | content: "",
12 | error: ""
13 | }
14 | }
15 |
16 | componentDidMount() {
17 | BackendService.getUserBoard()
18 | .then( response => {
19 | this.setState({content: response.data})
20 | } , error => {
21 | console.log(error);
22 | this.setState({
23 | error: error.toString()
24 | });
25 | });
26 | }
27 |
28 | render() {
29 | return (
30 |
31 |
32 |
33 | {
34 | this.state.content ? (
35 |
36 |
37 | {this.state.content}
38 |
39 |
40 | ) : (
41 |
42 |
43 | {this.state.error}
44 |
45 |
46 | )
47 | }
48 |
49 |
50 | );
51 | }
52 | }
53 |
54 | export default UserPage;
--------------------------------------------------------------------------------
/src/app/services/AuthenticationService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | /**
4 | * @Copyright by https://loizenai.com
5 | * youtube loizenai
6 | */
7 |
8 | class AuthenticationService {
9 | signin = (username, password) => {
10 | return axios.post("/api/auth/signin", {username, password})
11 | .then(response => {
12 | if (response.data.accessToken) {
13 | localStorage.setItem("user", JSON.stringify(response.data));
14 | }
15 | return response.data;
16 | })
17 | .catch(err => {
18 | console.log(err);
19 | throw err;
20 | });
21 | }
22 |
23 | signOut() {
24 | localStorage.removeItem("user");
25 | }
26 |
27 | register = async(firstname, lastname, username, email, password) => {
28 | return axios.post("/api/auth/signup", {
29 | firstname,
30 | lastname,
31 | username,
32 | email,
33 | password
34 | });
35 | }
36 |
37 | getCurrentUser() {
38 | return JSON.parse(localStorage.getItem('user'));;
39 | }
40 | }
41 |
42 | export default new AuthenticationService();
--------------------------------------------------------------------------------
/src/app/services/BackendService.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | // Add a request interceptor
4 | axios.interceptors.request.use( config => {
5 | const user = JSON.parse(localStorage.getItem('user'));
6 |
7 | if(user && user.accessToken){
8 | const token = 'Bearer ' + user.accessToken;
9 | config.headers.Authorization = token;
10 | }
11 |
12 | return config;
13 | });
14 |
15 | class BackendService {
16 | async getUserBoard() {
17 | return await axios.get("/api/test/user");
18 | }
19 |
20 | async getPmBoard() {
21 | return await axios.get("/api/test/pm");
22 | }
23 |
24 | async getAdminBoard() {
25 | return await axios.get("/api/test/admin");
26 | }
27 | }
28 |
29 | export default new BackendService();
--------------------------------------------------------------------------------
/src/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loizenai/reactjs-jwt-authentication/ed7fdb48c666b1dea85b0f2b1ae145b510d908a3/src/avatar.png
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 | import 'bootstrap/dist/css/bootstrap.min.css';
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById('root')
13 | );
14 |
15 | // If you want to start measuring performance in your app, pass a function
16 | // to log results (for example: reportWebVitals(console.log))
17 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
18 | reportWebVitals();
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------