From dcd03d78885620c12b2f6c20c211a610a5de7668 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Wed, 2 Oct 2019 00:49:51 -0400 Subject: [PATCH] Login frontend complete --- functions/handlers/users.js | 4 +- twistter-frontend/package-lock.json | 5 + twistter-frontend/package.json | 3 +- twistter-frontend/src/App.js | 25 +++- twistter-frontend/src/pages/Login.js | 187 +++++++++++++++++++++--- twistter-frontend/src/util/AuthRoute.js | 11 ++ 6 files changed, 206 insertions(+), 29 deletions(-) create mode 100644 twistter-frontend/src/util/AuthRoute.js diff --git a/functions/handlers/users.js b/functions/handlers/users.js index cdeb3f6..1c217e5 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -121,11 +121,11 @@ exports.login = (req, res) => { return data.user.getIdToken(); }) .then((token) => { - return res.json({ token }); + return res.status(200).json({ token }); }) .catch((err) => { console.error(err); - if (err.code === "auth/wrong-password") { + if (err.code === "auth/wrong-password" || err.code === "auth/invalid-email") { return res .status(403) .json({ general: "Invalid credentials. Please try again." }); diff --git a/twistter-frontend/package-lock.json b/twistter-frontend/package-lock.json index 2fb6aa4..8b43be3 100644 --- a/twistter-frontend/package-lock.json +++ b/twistter-frontend/package-lock.json @@ -5277,6 +5277,11 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=" }, + "jwt-decode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", + "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", diff --git a/twistter-frontend/package.json b/twistter-frontend/package.json index bc4672f..a83c175 100644 --- a/twistter-frontend/package.json +++ b/twistter-frontend/package.json @@ -8,6 +8,7 @@ "clsx": "^1.0.4", "create-react-app": "^3.1.2", "install": "^0.13.0", + "jwt-decode": "^2.2.0", "node-pre-gyp": "^0.13.0", "react": "^16.9.0", "react-dom": "^16.9.0", @@ -36,5 +37,5 @@ "last 1 safari version" ] }, - "proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api" + "proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api" } diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index a0a4720..4a13d53 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -6,16 +6,31 @@ import './App.css'; import { BrowserRouter as Router } from 'react-router-dom'; import Route from 'react-router-dom/Route'; import Navbar from './components/layout/NavBar'; +import jwtDecode from 'jwt-decode'; + +// Components +import AuthRoute from './util/AuthRoute'; import home from './pages/Home'; import register from './pages/Register'; import login from './pages/Login'; -import user from './pages/User'; - +import user from './pages/user'; import writeMicroblog from './Writing_Microblogs.js'; import edit from './pages/edit.js'; import userLine from './Userline.js'; +let authenticated; +const token = localStorage.FBIdToken; +if (token) { + const decodedToken = jwtDecode(token); + if (decodedToken.exp * 1000 < Date.now()) { + window.location.href = '/login'; + authenticated = false; + } else { + authenticated = true; + } +} + class App extends Component { render() { return ( @@ -24,9 +39,9 @@ class App extends Component {
- - - + + + diff --git a/twistter-frontend/src/pages/Login.js b/twistter-frontend/src/pages/Login.js index 76531d2..2686e84 100644 --- a/twistter-frontend/src/pages/Login.js +++ b/twistter-frontend/src/pages/Login.js @@ -1,26 +1,171 @@ -/* eslint-disable */ -import React, { Component } from 'react'; -import '../App.css'; - +import React, { Component } from "react"; +import axios from "axios"; +import PropTypes from "prop-types"; import logo from '../images/twistter-logo.png'; -import TextField from '@material-ui/core/TextField'; -class Login extends Component { - render() { - return ( -
- logo -

- Log in to Twistter -

- -

- -

- -
- ); +// Material-UI stuff +import Button from "@material-ui/core/Button"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import Grid from "@material-ui/core/Grid"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import withStyles from "@material-ui/core/styles/withStyles"; + +const styles = { + form: { + textAlign: "center" + }, + textField: { + marginBottom: 30 + }, + pageTitle: { + // marginTop: 20, + marginBottom: 40 + }, + button: { + positon: "relative", + marginBottom: 30 + }, + progress: { + position: "absolute" + } +}; + +export class Login extends Component { + // componentDidMount() { + // axios + // .get("/getProfileInfo") + // .then((res) => { + // this.setState({ + // firstName: res.data.firstName, + // lastName: res.data.lastName, + // email: res.data.email, + // handle: res.data.handle, + // bio: res.data.bio + // }); + // }) + // .catch((err) => { + // console.error(err); + // }); + // } + + // Constructor for the state + constructor() { + super(); + this.state = { + email: "", + password:"", + loading: false, + errors: {} + }; + } + + // Runs whenever the submit button is clicked. + // Updates the database entry of the signed in user with the + // data stored in the state. + handleSubmit = (event) => { + event.preventDefault(); + this.setState({ + loading: true + }); + const loginData = { + email: this.state.email, + password: this.state.password, + }; + axios + .post("/login", loginData) + .then((res) => { + // Save the login token + localStorage.setItem('FBIdToken', `Bearer ${res.data.token}`); + this.setState({ + loading: false + }); + // Redirects to home page + this.props.history.push('/home'); + }) + .catch((err) => { + this.setState({ + errors: err.response.data, + loading: false + }); + }); }; + + // Updates the state whenever one of the textboxes changes. + // The key is the name of the textbox and the value is the + // value in the text box. + handleChange = (event) => { + this.setState({ + [event.target.name]: event.target.value, + errors: { + [event.target.name]: null + } + }); + }; + + render() { + const { classes } = this.props; + const { errors, loading } = this.state; + + return ( + + + + logo + + Log in to Twistter + +
+ + + + {errors.general && ( + Wrong Email or Password + )} + +
+ + + ); + } } -export default Login; \ No newline at end of file +Login.propTypes = { + classes: PropTypes.object.isRequired +}; + +export default withStyles(styles)(Login); diff --git a/twistter-frontend/src/util/AuthRoute.js b/twistter-frontend/src/util/AuthRoute.js new file mode 100644 index 0000000..8cc2395 --- /dev/null +++ b/twistter-frontend/src/util/AuthRoute.js @@ -0,0 +1,11 @@ +import React from 'react' +import { Route, Redirect} from 'react-router-dom'; + +const AuthRoute = ({ component: Component, authenticated, ...rest}) => ( + authenticated === true ? : } + /> +) + +export default AuthRoute;