Repository: cornflourblue/react-jwt-authentication-example Branch: master Commit: 4d4f1d9f3afa Files: 24 Total size: 16.9 KB Directory structure: gitextract_vywxl4o3/ ├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src/ │ ├── App/ │ │ ├── App.jsx │ │ └── index.js │ ├── HomePage/ │ │ ├── HomePage.jsx │ │ └── index.js │ ├── LoginPage/ │ │ ├── LoginPage.jsx │ │ └── index.js │ ├── _components/ │ │ ├── PrivateRoute.jsx │ │ └── index.js │ ├── _helpers/ │ │ ├── auth-header.js │ │ ├── fake-backend.js │ │ ├── handle-response.js │ │ ├── history.js │ │ └── index.js │ ├── _services/ │ │ ├── authentication.service.js │ │ ├── index.js │ │ └── user.service.js │ ├── index.html │ └── index.jsx └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": [ "@babel/preset-react", "@babel/preset-env" ] } ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages typings # Optional npm cache directory .npm # Optional REPL history .node_repl_history ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Jason Watmore Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # react-jwt-authentication-example React (without Redux) - JWT Authentication Tutorial & Example To see a demo and further details go to http://jasonwatmore.com/post/2019/04/06/react-jwt-authentication-tutorial-example ================================================ FILE: package.json ================================================ { "name": "react-jwt-authentication-example", "version": "1.0.0", "repository": { "type": "git", "url": "https://github.com/cornflourblue/react-jwt-authentication-example.git" }, "license": "MIT", "scripts": { "start": "webpack-dev-server --open" }, "dependencies": { "formik": "^1.5.2", "history": "^4.9.0", "react": "^16.8.6", "react-dom": "^16.8.6", "react-router-dom": "^5.0.0", "rxjs": "^6.3.3", "yup": "^0.27.0" }, "devDependencies": { "@babel/core": "^7.4.3", "@babel/preset-env": "^7.4.3", "@babel/preset-react": "^7.0.0", "babel-loader": "^8.0.5", "html-webpack-plugin": "^3.2.0", "path": "^0.12.7", "webpack": "^4.29.6", "webpack-cli": "^3.3.0", "webpack-dev-server": "^3.2.1" } } ================================================ FILE: src/App/App.jsx ================================================ import React from 'react'; import { Router, Route, Link } from 'react-router-dom'; import { history } from '@/_helpers'; import { authenticationService } from '@/_services'; import { PrivateRoute } from '@/_components'; import { HomePage } from '@/HomePage'; import { LoginPage } from '@/LoginPage'; class App extends React.Component { constructor(props) { super(props); this.state = { currentUser: null }; } componentDidMount() { authenticationService.currentUser.subscribe(x => this.setState({ currentUser: x })); } logout() { authenticationService.logout(); history.push('/login'); } render() { const { currentUser } = this.state; return (
{currentUser && }
); } } export { App }; ================================================ FILE: src/App/index.js ================================================ export * from './App'; ================================================ FILE: src/HomePage/HomePage.jsx ================================================ import React from 'react'; import { userService, authenticationService } from '@/_services'; class HomePage extends React.Component { constructor(props) { super(props); this.state = { currentUser: authenticationService.currentUserValue, users: null }; } componentDidMount() { userService.getAll().then(users => this.setState({ users })); } render() { const { currentUser, users } = this.state; return (

Hi {currentUser.firstName}!

You're logged in with React & JWT!!

Users from secure api end point:

{users && }
); } } export { HomePage }; ================================================ FILE: src/HomePage/index.js ================================================ export * from './HomePage'; ================================================ FILE: src/LoginPage/LoginPage.jsx ================================================ import React from 'react'; import { Formik, Field, Form, ErrorMessage } from 'formik'; import * as Yup from 'yup'; import { authenticationService } from '@/_services'; class LoginPage extends React.Component { constructor(props) { super(props); // redirect to home if already logged in if (authenticationService.currentUserValue) { this.props.history.push('/'); } } render() { return (
Username: test
Password: test

Login

{ setStatus(); authenticationService.login(username, password) .then( user => { const { from } = this.props.location.state || { from: { pathname: "/" } }; this.props.history.push(from); }, error => { setSubmitting(false); setStatus(error); } ); }} render={({ errors, status, touched, isSubmitting }) => (
{isSubmitting && }
{status &&
{status}
}
)} />
) } } export { LoginPage }; ================================================ FILE: src/LoginPage/index.js ================================================ export * from './LoginPage'; ================================================ FILE: src/_components/PrivateRoute.jsx ================================================ import React from 'react'; import { Route, Redirect } from 'react-router-dom'; import { authenticationService } from '@/_services'; export const PrivateRoute = ({ component: Component, ...rest }) => ( { const currentUser = authenticationService.currentUserValue; if (!currentUser) { // not logged in so redirect to login page with the return url return } // authorised so return component return }} /> ) ================================================ FILE: src/_components/index.js ================================================ export * from './PrivateRoute'; ================================================ FILE: src/_helpers/auth-header.js ================================================ import { authenticationService } from '@/_services'; export function authHeader() { // return authorization header with jwt token const currentUser = authenticationService.currentUserValue; if (currentUser && currentUser.token) { return { Authorization: `Bearer ${currentUser.token}` }; } else { return {}; } } ================================================ FILE: src/_helpers/fake-backend.js ================================================ export function configureFakeBackend() { let users = [{ id: 1, username: 'test', password: 'test', firstName: 'Test', lastName: 'User' }]; let realFetch = window.fetch; window.fetch = function (url, opts) { const isLoggedIn = opts.headers['Authorization'] === 'Bearer fake-jwt-token'; return new Promise((resolve, reject) => { // wrap in timeout to simulate server api call setTimeout(() => { // authenticate - public if (url.endsWith('/users/authenticate') && opts.method === 'POST') { const params = JSON.parse(opts.body); const user = users.find(x => x.username === params.username && x.password === params.password); if (!user) return error('Username or password is incorrect'); return ok({ id: user.id, username: user.username, firstName: user.firstName, lastName: user.lastName, token: 'fake-jwt-token' }); } // get users - secure if (url.endsWith('/users') && opts.method === 'GET') { if (!isLoggedIn) return unauthorised(); return ok(users); } // pass through any requests not handled above realFetch(url, opts).then(response => resolve(response)); // private helper functions function ok(body) { resolve({ ok: true, text: () => Promise.resolve(JSON.stringify(body)) }) } function unauthorised() { resolve({ status: 401, text: () => Promise.resolve(JSON.stringify({ message: 'Unauthorised' })) }) } function error(message) { resolve({ status: 400, text: () => Promise.resolve(JSON.stringify({ message })) }) } }, 500); }); } } ================================================ FILE: src/_helpers/handle-response.js ================================================ import { authenticationService } from '@/_services'; export function handleResponse(response) { return response.text().then(text => { const data = text && JSON.parse(text); if (!response.ok) { if ([401, 403].indexOf(response.status) !== -1) { // auto logout if 401 Unauthorized or 403 Forbidden response returned from api authenticationService.logout(); location.reload(true); } const error = (data && data.message) || response.statusText; return Promise.reject(error); } return data; }); } ================================================ FILE: src/_helpers/history.js ================================================ import { createBrowserHistory } from 'history'; export const history = createBrowserHistory(); ================================================ FILE: src/_helpers/index.js ================================================ export * from './auth-header'; export * from './fake-backend'; export * from './handle-response'; export * from './history'; ================================================ FILE: src/_services/authentication.service.js ================================================ import { BehaviorSubject } from 'rxjs'; import config from 'config'; import { handleResponse } from '@/_helpers'; const currentUserSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('currentUser'))); export const authenticationService = { login, logout, currentUser: currentUserSubject.asObservable(), get currentUserValue () { return currentUserSubject.value } }; function login(username, password) { const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }; return fetch(`${config.apiUrl}/users/authenticate`, requestOptions) .then(handleResponse) .then(user => { // store user details and jwt token in local storage to keep user logged in between page refreshes localStorage.setItem('currentUser', JSON.stringify(user)); currentUserSubject.next(user); return user; }); } function logout() { // remove user from local storage to log user out localStorage.removeItem('currentUser'); currentUserSubject.next(null); } ================================================ FILE: src/_services/index.js ================================================ export * from './authentication.service'; export * from './user.service'; ================================================ FILE: src/_services/user.service.js ================================================ import config from 'config'; import { authHeader, handleResponse } from '@/_helpers'; export const userService = { getAll }; function getAll() { const requestOptions = { method: 'GET', headers: authHeader() }; return fetch(`${config.apiUrl}/users`, requestOptions).then(handleResponse); } ================================================ FILE: src/index.html ================================================ React - JWT Authentication Tutorial & Example
================================================ FILE: src/index.jsx ================================================ import React from 'react'; import { render } from 'react-dom'; import { App } from './App'; // setup fake backend import { configureFakeBackend } from './_helpers'; configureFakeBackend(); render( , document.getElementById('app') ); ================================================ FILE: webpack.config.js ================================================ var HtmlWebpackPlugin = require('html-webpack-plugin'); const path = require('path'); module.exports = { mode: 'development', resolve: { extensions: ['.js', '.jsx'] }, module: { rules: [ { test: /\.jsx?$/, loader: 'babel-loader' } ] }, resolve: { extensions: ['.js', '.jsx'], alias: { '@': path.resolve(__dirname, 'src/'), } }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })], devServer: { historyApiFallback: true }, externals: { // global app config object config: JSON.stringify({ apiUrl: 'http://localhost:4000' }) } }