Auth error handling. Auth redirecting

This commit is contained in:
Paweł Malak
2021-11-08 17:46:16 +01:00
parent f09969079c
commit e9d61b1334
11 changed files with 105 additions and 12 deletions

View File

@@ -2,19 +2,30 @@ import { Fragment, useContext, useEffect } from 'react';
import { Switch, Route } from 'react-router-dom'; import { Switch, Route } from 'react-router-dom';
import { Navbar } from './components/Navigation/Navbar'; import { Navbar } from './components/Navigation/Navbar';
import { Editor, Home, Snippet, Snippets, Auth, Profile } from './containers'; import { Editor, Home, Snippet, Snippets, Auth, Profile } from './containers';
import { ProtectedRoute } from './utils'; import { decodeToken, ProtectedRoute } from './utils';
import { AuthContext } from './store'; import { AuthContext, SnippetsContext } from './store';
export const App = () => { export const App = () => {
const { autoLogin } = useContext(AuthContext); const { autoLogin, logout } = useContext(AuthContext);
const { getSnippets, countTags } = useContext(SnippetsContext);
useEffect(() => { useEffect(() => {
// autoLogin(); autoLogin();
// const checker = setInterval(() => {
// autoLogin(); getSnippets();
// console.log('cake'); countTags();
// }, 1000);
// return () => window.clearInterval(checker); const checkTokenValidity = setInterval(() => {
if (localStorage.token) {
const { exp: expiresAt } = decodeToken(localStorage.token);
if (Date.now() > expiresAt * 1000) {
logout();
}
}
}, 1000);
return () => window.clearInterval(checkTokenValidity);
}, []); }, []);
return ( return (

View File

@@ -1,5 +1,5 @@
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { useHistory } from 'react-router'; import { useHistory, useLocation } from 'react-router';
import { AuthContext } from '../store'; import { AuthContext } from '../store';
import { AuthForm } from '../components/Auth'; import { AuthForm } from '../components/Auth';
import { Card, Layout } from '../components/UI'; import { Card, Layout } from '../components/UI';
@@ -8,9 +8,13 @@ export const Auth = (): JSX.Element => {
const { isAuthenticated } = useContext(AuthContext); const { isAuthenticated } = useContext(AuthContext);
const history = useHistory(); const history = useHistory();
// Get previous location
const location = useLocation<{ from: string }>();
const { from } = location.state || '/';
useEffect(() => { useEffect(() => {
if (isAuthenticated) { if (isAuthenticated) {
history.push('/'); history.push(from);
} }
}, [isAuthenticated]); }, [isAuthenticated]);

View File

@@ -10,6 +10,13 @@ export const ProtectedRoute = ({ ...rest }: RouteProps) => {
if (isAuthenticated) { if (isAuthenticated) {
return <Route {...rest} />; return <Route {...rest} />;
} else { } else {
return <Redirect to='/auth' />; return (
<Redirect
to={{
pathname: '/auth',
state: { from: window.location.pathname }
}}
/>
);
} }
}; };

View File

@@ -0,0 +1,20 @@
import { errorHandler } from '.';
import { UserWithRole } from '../typescript/interfaces';
interface Params {
err: any;
setIsAuthenticated: (v: React.SetStateAction<boolean>) => void;
setUser: (v: React.SetStateAction<UserWithRole | null>) => void;
}
export const authErrorHandler = (params: Params) => {
const { err, setUser, setIsAuthenticated } = params;
errorHandler(err);
localStorage.removeItem('token');
setUser(null);
setIsAuthenticated(false);
};

View File

@@ -11,5 +11,7 @@ export const errorHandler = (err: any) => {
msg = 'Something went wrong'; msg = 'Something went wrong';
} }
// todo: emit notification
// redirect on error
console.log(msg); console.log(msg);
}; };

View File

@@ -3,4 +3,6 @@ export * from './badgeColor';
export * from './findLanguage'; export * from './findLanguage';
export * from './searchParser'; export * from './searchParser';
export * from './ProtectedRoute'; export * from './ProtectedRoute';
export * from './authErrorHandler';
export * from './errorHandler'; export * from './errorHandler';
export * from './decodeToken';

View File

@@ -7,4 +7,6 @@ services:
- /path/to/host/data:/app/data - /path/to/host/data:/app/data
ports: ports:
- 5000:5000 - 5000:5000
environment:
- NODE_ENV: production
restart: unless-stopped restart: unless-stopped

13
list.md Normal file
View File

@@ -0,0 +1,13 @@
**Implemented**
- [x] Login / logout / register
- [x] Auto login (token valid 14 days)
- [x] Roles (admin, user)
**Planned features**
- [ ] Guest access (read and search access to snippets marked as public)
- [ ] Generate token for raw snippet access
- [ ] Admin panel (right now admin and users share the same view)
By default, admin account is created with login `admin@local.com` and password `snippet-admin`. If there are any snippets in the database, they will be assigned to the admin as the owner.

View File

@@ -11,6 +11,20 @@ export const errorHandler = (
) => { ) => {
logger.log(err.message, 'ERROR'); logger.log(err.message, 'ERROR');
if (process.env.NODE_ENV == 'development') {
console.log(err);
}
const error = {
...err
};
if (err.message == 'Validation error') {
// @ts-ignore
error.message = err.errors[0].message;
error.statusCode = 400;
}
res.status(err.statusCode || 500).json({ res.status(err.statusCode || 500).json({
error: err.message || 'Internal Server Error' error: err.message || 'Internal Server Error'
}); });

View File

@@ -7,3 +7,4 @@ export * from './createAdmin';
export * from './hashPassword'; export * from './hashPassword';
export * from './signToken'; export * from './signToken';
export * from './createAdmin'; export * from './createAdmin';
export * from './resourceExists';

View File

@@ -0,0 +1,17 @@
import { NextFunction } from 'express';
import { ErrorResponse } from '.';
export const resourceExists = (
resource: any,
name: string,
id: number,
next: NextFunction
) => {
if (!resource) {
return next(
new ErrorResponse(404, `${name} with the id of ${id} was not found`)
);
}
return;
};