From 9d7b14fcb3d40b7415adff9a8d0e552f5cfb8005 Mon Sep 17 00:00:00 2001 From: Julie Nisbet Date: Sun, 25 Oct 2020 20:07:41 -0700 Subject: [PATCH 1/2] Adding simple CSRF example(no auth) --- client/src/App.js | 72 +++++++++++++++++++++--------- client/src/auth.js | 5 +++ client/src/components/User.js | 10 ++++- client/src/components/UserForm.js | 56 +++++++++++++++++++++++ client/src/components/UsersList.js | 3 +- starter_app/__init__.py | 4 ++ starter_app/api/user_routes.py | 10 ++++- 7 files changed, 134 insertions(+), 26 deletions(-) create mode 100644 client/src/auth.js create mode 100644 client/src/components/UserForm.js diff --git a/client/src/App.js b/client/src/App.js index 281352b..e34fe38 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,30 +1,62 @@ -import React from 'react'; +import React, {useEffect, useState} from 'react'; import { BrowserRouter, Switch, Route, NavLink } from 'react-router-dom'; import UserList from './components/UsersList'; - +import UserForm from './components/UserForm'; +import AuthContext from './auth' function App() { + const [fetchWithCSRF, setFetchWithCSRF] = useState(() => fetch); + const authContextValue = { + fetchWithCSRF, + }; + useEffect(() => { + async function restoreCSRF() { + const response = await fetch('/api/csrf/restore', { + method: 'GET', + credentials: 'include' + }); + if (response.ok) { + const authData = await response.json(); + setFetchWithCSRF(() => { + return (resource, init) => { + if (init.headers) { + init.headers['X-CSRFToken'] = authData.csrf_token; + } else { + init.headers = { + 'X-CSRFToken': authData.csrf_token + } + } + return fetch(resource, init); + } + }); + } + } + restoreCSRF(); + }, []); return ( - - - - - - - - -

My Home Page

-
-
-
+ + + + + + + + + +

My Home Page

+
+
+
+
); } -export default App; +export default App; \ No newline at end of file diff --git a/client/src/auth.js b/client/src/auth.js new file mode 100644 index 0000000..082e4fe --- /dev/null +++ b/client/src/auth.js @@ -0,0 +1,5 @@ +import React from 'react'; + +const AuthContext = React.createContext({}); + +export default AuthContext; \ No newline at end of file diff --git a/client/src/components/User.js b/client/src/components/User.js index 7684d78..ad115f5 100644 --- a/client/src/components/User.js +++ b/client/src/components/User.js @@ -1,11 +1,17 @@ -import React from 'react'; - +import React, {} from 'react'; +import { Link } from 'react-router-dom'; function User(props) { return ( <> Username: {props.user.username}
Email: {props.user.email}
+ Edit
); diff --git a/client/src/components/UserForm.js b/client/src/components/UserForm.js new file mode 100644 index 0000000..c7588de --- /dev/null +++ b/client/src/components/UserForm.js @@ -0,0 +1,56 @@ +import React, {useState, useContext} from 'react'; +import AuthContext from '../auth' + +function UserForm(props) { + const { id } = props.location.state.user; + console.log(id) + const [username, setUsername] = useState(props.location.state.user.username); + const [email, setEmail] = useState(props.location.state.user.email); + + const [errors, setErrors] = useState([]); + const {fetchWithCSRF} = useContext(AuthContext); + + const submitForm = (e) => { + e.preventDefault(); + + async function loginUser() { + const response = await fetchWithCSRF(`/api/users/${id}`, { + method: 'POST', + headers: { + "Content-Type": "application/json", + }, + credentials: 'include', + body: JSON.stringify({ + username, + email + }) + }); + + const responseData = await response.json(); + if (!response.ok) { + setErrors(responseData.errors); + } + } + loginUser(); + } + return ( +
+ {errors.length ? errors.map((err) =>
  • {err}
  • ) : ''} +
    + +
    + setEmail(e.target.value)} name="email" /> +
    +
    +
    + +
    + setUsername(e.target.value)} name="username" /> +
    +
    + + +
    + ); +} +export default UserForm; \ No newline at end of file diff --git a/client/src/components/UsersList.js b/client/src/components/UsersList.js index c458245..2090077 100644 --- a/client/src/components/UsersList.js +++ b/client/src/components/UsersList.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, createContext } from 'react'; import User from './User'; @@ -13,7 +13,6 @@ function UsersList (props) { } fetchData(); }, []); - const userComponents = users.map((user) => ) return ( <> diff --git a/starter_app/__init__.py b/starter_app/__init__.py index edb283d..f1d5b49 100644 --- a/starter_app/__init__.py +++ b/starter_app/__init__.py @@ -12,6 +12,7 @@ app = Flask(__name__) app.config.from_object(Config) +CSRFProtect(app) app.register_blueprint(user_routes, url_prefix='/api/users') db.init_app(app) @@ -34,3 +35,6 @@ def react_root(path): if path == 'favicon.ico': return app.send_static_file('favicon.ico') return app.send_static_file('index.html') +@app.route('/api/csrf/restore') +def restore_csrf(): + return {'csrf_token': generate_csrf()} diff --git a/starter_app/api/user_routes.py b/starter_app/api/user_routes.py index b6fd740..2838a11 100644 --- a/starter_app/api/user_routes.py +++ b/starter_app/api/user_routes.py @@ -3,7 +3,13 @@ user_routes = Blueprint('users', __name__) + @user_routes.route('/') def index(): - response = User.query.all() - return { "users": [user.to_dict() for user in response]} \ No newline at end of file + response = User.query.all() + return {"users": [user.to_dict() for user in response]} + + +@user_routes.route('/', methods=['GET', 'POST']) +def user_detail(id): + return {} \ No newline at end of file From 5c2203d37011c8e952794f31197c5e699ed5a82d Mon Sep 17 00:00:00 2001 From: Julie Nisbet Date: Tue, 27 Oct 2020 15:58:08 -0700 Subject: [PATCH 2/2] change login to update --- .gitignore | 1 + client/src/components/UserForm.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 70ba85b..bddaeda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env __pycache__/ *.py[cod] +.vscode/* \ No newline at end of file diff --git a/client/src/components/UserForm.js b/client/src/components/UserForm.js index c7588de..21a62c4 100644 --- a/client/src/components/UserForm.js +++ b/client/src/components/UserForm.js @@ -13,7 +13,7 @@ function UserForm(props) { const submitForm = (e) => { e.preventDefault(); - async function loginUser() { + async function updateUser() { const response = await fetchWithCSRF(`/api/users/${id}`, { method: 'POST', headers: { @@ -31,7 +31,7 @@ function UserForm(props) { setErrors(responseData.errors); } } - loginUser(); + updateUser(); } return (