Refactor authentication with Context

This commit is contained in:
rui hildt 2020-08-21 11:51:52 +02:00
parent ec65c3b9dd
commit 51ec69a3cb
11 changed files with 137 additions and 109 deletions

View File

@ -1,10 +1,12 @@
import React from 'react'; import React, { useState } from 'react';
import 'rsuite/lib/styles/index.less'; import 'rsuite/lib/styles/index.less';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import PrivateRoute from './components/routes/PrivateRoute'; import { AuthContext } from './helpers/authContext';
import PublicRoute from './components/routes/PublicRoute';
import PrivateRoute from './components/Routes/PrivateRoute';
import Home from './screens/Home';
import Dashboard from './screens/Dashboard'; import Dashboard from './screens/Dashboard';
import Schedule from './screens/Schedule/Schedule'; import Schedule from './screens/Schedule/Schedule';
import Availability from './screens/Availability/Availability'; import Availability from './screens/Availability/Availability';
@ -12,21 +14,35 @@ import Invite from './screens/Invite/Invite';
import Login from './screens/Login'; import Login from './screens/Login';
import Register from './screens/Register'; import Register from './screens/Register';
const existingToken = localStorage.getItem('token');
// const existingUser = JSON.parse(localStorage.getItem('user'));
export default function App() { export default function App() {
const [authToken, setAuthToken] = useState(existingToken);
const setToken = (data) => {
localStorage.setItem('token', JSON.stringify(data));
setAuthToken(data);
};
return ( return (
<Router> <AuthContext.Provider value={{ authToken, setAuthToken: setToken }}>
<Switch> <Router>
<Route path='/' exact component={Login} /> <Switch>
<Route path='/' exact component={Home} />
<Route path='/login' component={Login} />
<Route path='/register' component={Register} />
<PublicRoute path='/login' component={Login} /> <PrivateRoute path='/dashboard' component={Dashboard} />
<PublicRoute path='/register' component={Register} /> <PrivateRoute path='/schedule' component={Schedule} />
<PrivateRoute path='/invite' component={Invite} />
<PrivateRoute path='/dashboard' component={Dashboard} /> <PrivateRoute
<PrivateRoute path='/schedule' component={Schedule} /> path='/availability'
<PrivateRoute path='/invite' component={Invite} /> component={Availability}
<PrivateRoute path='/availability' component={Availability} /> />
<PrivateRoute path='/schedule' component={Schedule} /> <PrivateRoute path='/schedule' component={Schedule} />
</Switch> </Switch>
</Router> </Router>
</AuthContext.Provider>
); );
} }

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Nav, Icon, Dropdown, Popover, Whisper } from 'rsuite'; import { Nav, Icon, Dropdown, Popover, Whisper } from 'rsuite';
export default function MenuDropdown() { export default function MenuDropdown() {
@ -12,18 +11,34 @@ export default function MenuDropdown() {
triggerRef.current.hide(); triggerRef.current.hide();
} }
function handleLogout() {
localStorage.removeItem('token');
localStorage.removeItem('user');
history.push('/');
}
const MenuPopover = ({ onSelect, ...rest }) => ( const MenuPopover = ({ onSelect, ...rest }) => (
<Popover {...rest} full> <Popover {...rest} full>
<Dropdown.Menu onSelect={onSelect}> <Dropdown.Menu onSelect={onSelect}>
<Dropdown.Item eventKey={'/'}>Dashboard</Dropdown.Item> <Dropdown.Item eventKey={'/'}>Dashboard</Dropdown.Item>
<Dropdown.Item eventKey={'register'}>Register</Dropdown.Item> <Dropdown.Item eventKey={'register'}>Register</Dropdown.Item>
<Dropdown.Item eventKey={'login'}>Login</Dropdown.Item> <Dropdown.Item eventKey={'login'}>Login</Dropdown.Item>
<Dropdown.Item eventKey={'schedule'}>1 - Schedule a meeting</Dropdown.Item> <Dropdown.Item eventKey={'schedule'}>
<Dropdown.Item eventKey={'invite'}>2 - Invite participants</Dropdown.Item> 1 - Schedule a meeting
<Dropdown.Item eventKey={'availability'}>3 - Add your availability</Dropdown.Item> </Dropdown.Item>
<Dropdown.Item eventKey={'confirm'}>4 - Confirm meeting date</Dropdown.Item> <Dropdown.Item eventKey={'invite'}>
2 - Invite participants
</Dropdown.Item>
<Dropdown.Item eventKey={'availability'}>
3 - Add your availability
</Dropdown.Item>
<Dropdown.Item eventKey={'confirm'}>
4 - Confirm meeting date
</Dropdown.Item>
<Dropdown.Item eventKey={'settings'}>Settings</Dropdown.Item> <Dropdown.Item eventKey={'settings'}>Settings</Dropdown.Item>
<Dropdown.Item eventKey={5}>Log Out</Dropdown.Item> <Dropdown.Item eventKey={5} onSelect={() => handleLogout()}>
Log Out
</Dropdown.Item>
</Dropdown.Menu> </Dropdown.Menu>
</Popover> </Popover>
); );

View File

@ -0,0 +1,19 @@
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useAuth } from '../../helpers/authContext';
const PrivateRoute = ({ component: Component, ...rest }) => {
const { authToken } = useAuth();
return (
<Route
{...rest}
render={(props) =>
authToken ? <Component {...props} /> : <Redirect to='/login' />
}
/>
);
};
export default PrivateRoute;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useAuth } from '../../helpers/authContext';
// TODO Make sure it's used, else delete it and remove message in App
const PublicRoute = ({ component: Component, ...rest }) => {
const isAuthenticated = useAuth();
return (
<Route
{...rest}
render={(props) =>
isAuthenticated ? (
<Redirect to='/dashboard' />
) : (
<Component {...props} />
)
}
/>
);
};
export default PublicRoute;

View File

@ -1,29 +0,0 @@
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { getToken } from '../../utils/common';
const isLoggedIn = getToken();
const PrivateRoute = ({ component: Component, user, ...rest }) => {
return (
<Route
{...rest}
render={(props) =>
isLoggedIn ? (
<Component {...rest} {...props} />
) : (
<Redirect
to={{
pathname: '/login',
state: {
from: props.location,
},
}}
/>
)
}
/>
);
};
export default PrivateRoute;

View File

@ -1,22 +0,0 @@
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { getToken } from '../../utils/common';
const isLoggedIn = getToken();
function PublicRoute({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={(props) =>
!isLoggedIn ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/dashboard' }} />
)
}
/>
);
}
export default PublicRoute;

View File

@ -0,0 +1,7 @@
import { createContext, useContext } from 'react';
export const AuthContext = createContext();
export function useAuth() {
return useContext(AuthContext);
}

19
src/screens/Home.js Normal file
View File

@ -0,0 +1,19 @@
import React from 'react';
import { Panel } from 'rsuite';
import NavBar from '../components/Navbar/NavBar';
const boxStyle = {
margin: '50px 10px',
};
export default function Home() {
return (
<>
<NavBar title='Meeting Planner' />
<Panel header={<h3>Home</h3>} bordered style={boxStyle}>
<p>This will be the home page</p>
</Panel>
</>
);
}

View File

@ -1,6 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import { backend } from '../utils/http-common';
import { import {
Panel, Panel,
Form, Form,
@ -11,18 +10,19 @@ import {
Message, Message,
} from 'rsuite'; } from 'rsuite';
import { setUserSession } from '../utils/common';
import NavBar from './../components/Navbar/NavBar'; import NavBar from './../components/Navbar/NavBar';
import { backend } from '../helpers/http-common';
import { useAuth } from '../helpers/authContext';
export default function Login() { export default function Login() {
const history = useHistory();
const [error, setError] = useState(false); const [error, setError] = useState(false);
const [credentials, setCredentials] = useState({ const [credentials, setCredentials] = useState({
email: '', email: '',
password: '', password: '',
}); });
const { setAuthToken, authToken } = useAuth();
const handleChange = (value, evt) => { const handleChange = (value, evt) => {
setCredentials({ setCredentials({
...credentials, ...credentials,
@ -34,16 +34,20 @@ export default function Login() {
backend backend
.post('/auth/login', credentials) .post('/auth/login', credentials)
.then((response) => { .then((response) => {
setUserSession(response.data.token, response.data.user); setAuthToken(response.data.token);
history.push('/dashboard');
}) })
.catch((error) => { .catch((error) => {
if (error.response.status === 401) if (error.response.status === 401) {
setError('Incorrect credentials. Please try again.'); setError('The credentials provided were incorrect.');
else setError('Something went wrong. Please try again later.'); } else
setError('Something went wrong. Please try again later.');
}); });
}; };
if (authToken) {
return <Redirect to='/dashboard' />;
}
return ( return (
<> <>
<NavBar title='Login' /> <NavBar title='Login' />

View File

@ -1,26 +0,0 @@
// Set/Remove user and token to local storage
export const setUserSession = (token, user) => {
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
};
export const getUser = () => {
const userJSON = localStorage.getItem('user');
if (userJSON) {
const user = JSON.parse(userJSON);
return user;
} else return null;
};
export const getToken = () => {
const token = localStorage.getItem('token');
if (token) return token;
else return null;
};
export const removeUserSession = () => {
localStorage.removeItem('token');
localStorage.removeItem('user');
};