diff --git a/crisis-react/package-lock.json b/crisis-react/package-lock.json index 9e70625826188f7155c091619f7d3a19e967a7b7..506a1b284af90cbf57c70f62e0f49959806f6bbe 100644 --- a/crisis-react/package-lock.json +++ b/crisis-react/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.22.1" + "react-router-dom": "^6.22.1", + "react-token-auth": "^2.3.8" }, "devDependencies": { "@types/react": "^18.2.55", @@ -3422,6 +3423,11 @@ "react-dom": ">=16.8" } }, + "node_modules/react-token-auth": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-token-auth/-/react-token-auth-2.3.8.tgz", + "integrity": "sha512-sRy4xpPY0NgTXL/kwQ0LqUZpHiurLG/4lSr9MAVPAuvN/hnjQqAcszl+c869ZO6Pegj2/OEP0MDmvMBJv1SkHQ==" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", diff --git a/crisis-react/package.json b/crisis-react/package.json index b878f70a65cd2e478adabf0025a6d3afa8fdbaa2..58a9b167d759d43f3fa6d79c1a8e31cded6794ed 100644 --- a/crisis-react/package.json +++ b/crisis-react/package.json @@ -1,7 +1,7 @@ { "name": "crisis-react", "private": true, - "proxy":"http://localhost:5000/", + "proxy": "http://localhost:5000/", "version": "0.0.0", "type": "module", "scripts": { @@ -13,7 +13,8 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.22.1" + "react-router-dom": "^6.22.1", + "react-token-auth": "^2.3.8" }, "devDependencies": { "@types/react": "^18.2.55", diff --git a/crisis-react/src/App.jsx b/crisis-react/src/App.jsx index 66bd521fb31cf1aacce90c45ba58eb20e4a53c53..649306bf2af385a12dcb75722473c0b7595a3c69 100644 --- a/crisis-react/src/App.jsx +++ b/crisis-react/src/App.jsx @@ -1,24 +1,39 @@ import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Login from './login'; +import Home from './Home' +import Nav from './Nav' import './App.css' //Install this: npm install react-router-dom //npm install react-router-dom@latest + +/*react-token-auth handles all token behavior for this program for keeping track of **/ +//npm install react-token-auth + + //Important for running, do these commands /*npm create vite@latest cd crisis-react npm install npm run dev*/ + + + function App() { return ( <Router> - <Routes> - <Route path="/" element={<Login />} /> - </Routes> + <div className=""> + <Nav /> + <Routes> + <Route path="/" element={<Home />} /> + <Route path="/login" element={<Login />} /> + {/* Add other routes here */} + </Routes> + </div> </Router> ); diff --git a/crisis-react/src/Auth.tsx b/crisis-react/src/Auth.tsx new file mode 100644 index 0000000000000000000000000000000000000000..734d0ebe5ac2867f313112f54c8050c24adf017c --- /dev/null +++ b/crisis-react/src/Auth.tsx @@ -0,0 +1,13 @@ +import { createAuthProvider } from 'react-token-auth'; + +type Session = { accessToken: string; refreshToken: string }; + +export const { useAuth, authFetch, login, logout } = createAuthProvider<Session>({ + getAccessToken: session => session.accessToken, + storage: localStorage, + onUpdateToken: token => + fetch('/update-token', { + method: 'POST', + body: token.refreshToken, + }).then(r => r.json()), +}); \ No newline at end of file diff --git a/crisis-react/src/Home.jsx b/crisis-react/src/Home.jsx new file mode 100644 index 0000000000000000000000000000000000000000..478711c4d9341bb7c9b6972d1edf74be77a1d673 --- /dev/null +++ b/crisis-react/src/Home.jsx @@ -0,0 +1,36 @@ +import React, { useEffect, useState } from 'react' +import { Link } from 'react-router-dom' +import { useAuth } from './Auth' + + +const LoggedIn = () => { + return ( + <div> + <p>You are in!</p> + </div> + ); +}; + +const LoggedOut = () => { + return ( + <div> + <p>Currently logged out.</p> + </div> + ); +}; + +const Home = () => +{ + const [logged] = useAuth() + return ( + <div> + <h1>Welcome to our crisis project</h1> + {logged ? <LoggedIn/> : <LoggedOut/>} + </div> + + ) + + +} + +export default Home \ No newline at end of file diff --git a/crisis-react/src/Nav.jsx b/crisis-react/src/Nav.jsx new file mode 100644 index 0000000000000000000000000000000000000000..094ccc8990e4239a27b62d7f7b86d7979ea5ab8b --- /dev/null +++ b/crisis-react/src/Nav.jsx @@ -0,0 +1,61 @@ +import React from 'react' +import { Link } from 'react-router-dom' +import { useAuth , logout} from './Auth' + + + +const LoggedInLinks = () => { + return ( + <> + <li className="nav-item"> + <Link className="nav-link active" to="/">Home</Link> + </li> + + <li className="nav-item"> + <a className="nav-link active" href="#" onClick={()=>{logout()}}>Log Out</a> + </li> + </> + ) +} + + +const LoggedOutLinks = () => { + return ( + <> + <li className="nav-item"> + <Link className="nav-link active" to="/">Home</Link> + </li> + <li className="nav-item"> + <Link className="nav-link active" to="/login" >Login</Link> + </li> + + </> + ) +} + +const Nav = () => { + + const [logged] = useAuth(); + + return ( + <nav className="navbar navbar-expand-lg navbar-dark bg-dark"> + <div className="container-fluid"> + <Link className="navbar-brand" to="/">HTest</Link> + <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> + <span className="navbar-toggler-icon"></span> + </button> + <div className="collapse navbar-collapse" id="navbarNav"> + <ul className="navbar-nav"> + {logged?<LoggedInLinks/>:<LoggedOutLinks/>} + </ul> + </div> + </div> + </nav> + ) + + + +} + + +export default Nav diff --git a/crisis-react/src/login.jsx b/crisis-react/src/login.jsx index b920547f1daf92af3b4259fa11ec711953ad3996..cc1e2ca8e4c35d9cb54875769fa3cc47dbf775eb 100644 --- a/crisis-react/src/login.jsx +++ b/crisis-react/src/login.jsx @@ -1,24 +1,45 @@ -import React, { useState } from 'react' +import React, { useState } from 'react'; import './styles.css'; - +//import {useHistory} from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; +import {login} from './Auth' //takes in signup information -async function PostSignInfo(data) { +async function PostSignInfo(data, navigate) { try { - const respone = await fetch("http://127.0.0.1:5000/login", { - method: "POST", // or 'PUT' - headers: { - "Content-Type": "application/json", + const requestType ={ + method:"POST", + headers:{ + 'content-type':'application/json' }, - body: JSON.stringify(data), - }); - - - } - - catch (error) { + body:JSON.stringify(data) + } + + const response = await fetch("http://127.0.0.1:5000/login", requestType) + .then(res=>res.json()) + .then( + session => { + console.log(session.access_token); + login(session.access_token); + navigate('/'); + + }) + + let responseData; + + try { + responseData = await response.json(); + } catch (error) { + console.error("Error parsing JSON response:", error); + throw new Error("Failed to parse JSON response"); + } + + // Store tokens in localStorage + //localStorage.setItem('access_token', responseData.access_token); + //localStorage.setItem('refresh_token', responseData.refresh_token); + } catch (error) { console.error("Could not upload signup information successfully", error); } } @@ -28,10 +49,13 @@ function Login() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); - const handleSubmit = (e) => { + const navigate = useNavigate(); // Hook to get navigate function + //const history=useHistory() + + const handleSubmit = async (e) => { e.preventDefault(); - PostSignInfo({ username, password }); + await PostSignInfo({ username, password }, navigate); }; diff --git a/crisis-react/src/signup.jsx b/crisis-react/src/signup.jsx new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/crisis_events.db b/crisis_events.db index 4629ca175d1dfbc226f753b1e2b4fbfc4879a676..e256673bedfeefc6b76062980212e7c149505fe1 100644 Binary files a/crisis_events.db and b/crisis_events.db differ diff --git a/flask_backend.py b/flask_backend.py index 32a894e9f9e2b8afb4579d4f0b67d5281a6f7fd6..efb6f959e53cbf02561104322dc5f9e592712d2a 100644 --- a/flask_backend.py +++ b/flask_backend.py @@ -1,12 +1,16 @@ -from flask import Flask, request, abort +from flask import Flask, request, abort, jsonify, make_response from flask_cors import CORS from database_api import CrisisEventsDatabase #from database_implementation import sqlite_api from runtime_import import runtime_import import hashlib +from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity,create_refresh_token app = Flask(__name__) +app.config['JWT_SECRET_KEY'] = 'PI' +jwt = JWTManager(app) #pip install flask-cors +#pip install Flask-JWT-Extended CORS(app) @@ -62,11 +66,18 @@ def login(): if user_hash != stored_hash: print(f'Password is incorrect: {data}') return 'Incorrect Password', 401 - - - return 'Login successful', 200 # Return a response to indicate success - - + + access_token = create_access_token(username) + refresh_token = create_refresh_token(username) + print(f'Login Successful!\n') + return jsonify({"access_token": access_token, "refresh_token": refresh_token}), 200 # Return a response to indicate success + +@app.route('/refresh', methods = ['POST']) +@jwt_required(refresh = True) +def refresh(): + current_user = get_jwt_identity() + new_access_token = create_access_token(identity = current_user) + return make_response(jsonify({"access_token": new_access_token}), 200) @app.route('/database_service', methods=['POST','GET']) def database_service():