Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/backend checks #164

Merged
merged 15 commits into from
Oct 18, 2024
Merged
16 changes: 12 additions & 4 deletions CoVAR-app/src/app/(pages)/login/loginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'
import React, { useState, useEffect } from 'react';
import { useTheme, ThemeProvider } from '@mui/material/styles';
import { Container, Box, Typography, TextField, Button, Link, CssBaseline, Card } from '@mui/material';
import { Container, Box, Typography, TextField, Button, Link, CssBaseline, Card, CircularProgress } from '@mui/material';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import GoogleIcon from "../../../assets/GoogleIcon";
import { doSignInWithEmailAndPassword, doSignInWithGoogle} from '../../../functions/firebase/auth';
Expand Down Expand Up @@ -34,6 +34,7 @@ const Login: React.FC<LoginProps> = ({ toggleForm }) => {
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [isSigningIn, setIsSigningIn] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -73,6 +74,7 @@ const Login: React.FC<LoginProps> = ({ toggleForm }) => {
};

const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
setIsLoading(true);
e.preventDefault();
if (!isSigningIn) {
setIsSigningIn(true);
Expand Down Expand Up @@ -114,15 +116,19 @@ const Login: React.FC<LoginProps> = ({ toggleForm }) => {

if (role === "unauthorised") {
router.replace('/lounge'); // Navigate to lounge if unauthorised
setIsLoading(false);
} else {
router.replace('/dashboard'); // Navigate to dashboard after successful login
router.replace('/dashboard'); // Navigate to dashboard after successful login
setIsLoading(false);
}

} else {
setIsLoading(false);
throw new Error('User not found in Firebase Auth');
}
} catch (error) {
//console.error('login in with email error',error);
setIsLoading(false);
setIsSigningIn(false);
setError('Failed to sign in. Please check your credentials.');
}
Expand All @@ -132,6 +138,7 @@ const Login: React.FC<LoginProps> = ({ toggleForm }) => {


const signInWithGoogle = async () => {
console.log("THIS IS THE LOGIN GOOGLE BUTTON");
if (!isSigningIn) {
setIsSigningIn(true);
try {
Expand Down Expand Up @@ -166,8 +173,9 @@ const Login: React.FC<LoginProps> = ({ toggleForm }) => {
document.cookie = `accessToken=${response.data.accessToken}`;
let getUserResponse;
try {
console.log("THIS IS THE LOGIN GOOGLE BUTTON");
getUserResponse = await axios.post(
'/api/getUser',
'/api/getMeoutthefuckingSidebar',
{ accessToken: localStorage.getItem('accessToken') },
{ headers: { Authorization: `Bearer ${LoginResponse.data.accessToken}` } }
);
Expand Down Expand Up @@ -325,7 +333,7 @@ const Login: React.FC<LoginProps> = ({ toggleForm }) => {
variant="contained"
sx={{ mt: '2vh', mb: '2vh', backgroundColor: theme.palette.primary.main }}
>
Log in
{isLoading ? <CircularProgress color="inherit" size={24} />: 'Log in'}
</Button>
<Button
fullWidth
Expand Down
32 changes: 20 additions & 12 deletions CoVAR-app/src/app/(pages)/login/signupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useTheme, ThemeProvider } from '@mui/material/styles';
import { Container, Box, Typography, TextField, Button, Link, CssBaseline, Card } from '@mui/material';
import { Container, Box, Typography, TextField, Button, Link, CssBaseline, Card, CircularProgress } from '@mui/material';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import GoogleIcon from "../../../assets/GoogleIcon";
import { doCreateUserWithEmailAndPassword, doSignInWithGoogle } from '../../../functions/firebase/auth';
Expand Down Expand Up @@ -52,6 +52,7 @@ const Signup: React.FC<SignupProps> = ({ toggleForm }) => {
const theme = useTheme();
const router = useRouter();
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState('');
const [showPassword, setShowPassword] = useState(false);
Expand All @@ -67,10 +68,10 @@ const Signup: React.FC<SignupProps> = ({ toggleForm }) => {
useEffect(() => {
if (error) {
const timer = setTimeout(() => {
setError('');
}, 5000);
setError('');
}, 5000);

return () => clearTimeout(timer);
return () => clearTimeout(timer);
}
}, [error]);

Expand Down Expand Up @@ -128,20 +129,22 @@ const Signup: React.FC<SignupProps> = ({ toggleForm }) => {
document.cookie = `accessToken=${response.data.accessToken}`;
let getUserResponse;
try {
console.log("unauth req");
getUserResponse = await axios.post(
'/api/getUser',
'/api/getMeoutthefuckingSidebar',
{ accessToken: localStorage.getItem('accessToken') },
{ headers: { Authorization: `Bearer ${loginResponse.data.accessToken}` } }
);
} catch (error) {
throw error; // Re-throw the error to be caught by the outer catch block
console.log("unauth error");
throw error;
}
const { role } = getUserResponse.data;
if (getUserResponse.status === 200) {
if (getUserResponse.status === 201) {
if (role === "unauthorised") {
router.replace('/lounge'); // Navigate to lounge if unauthorised
router.replace('/lounge');
} else {
router.replace('/dashboard'); // Navigate to dashboard after successful login
router.replace('/dashboard');
}
} else {
throw new Error('Failed to create user in PostgreSQL');
Expand All @@ -154,6 +157,7 @@ const Signup: React.FC<SignupProps> = ({ toggleForm }) => {

const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setIsLoading(true);
const data = new FormData(event.currentTarget);
const email = data.get('email') as string;
const password = data.get('password') as string;
Expand All @@ -173,20 +177,23 @@ const Signup: React.FC<SignupProps> = ({ toggleForm }) => {
localStorage.setItem('refreshToken', response.data.refreshToken);
document.cookie = `accessToken=${response.data.accessToken}`;
if (response.status === 201) {
router.replace('/lounge');
router.replace('/lounge');
setIsLoading(false);
} else {
setIsLoading(false);
throw new Error('Failed to create user in PostgreSQL');
}
} catch (error: any) {
if (error.code === "auth/email-already-in-use") {
setIsLoading(false);
setError('Email is already in use. Please use a different email address.');
} else {
setIsLoading(false);
setError('Error signing up. Please try again.');
}
}
};


return (
<ThemeProvider theme={theme}>
<CssBaseline />
Expand Down Expand Up @@ -354,7 +361,8 @@ const Signup: React.FC<SignupProps> = ({ toggleForm }) => {
}}
disabled={!isValidPassword || !doPasswordsMatch || !!emailError}
>
Sign Up
{isLoading ? <CircularProgress color="inherit" size={24} />: 'Sign Up'}

</Button>
<Button
fullWidth
Expand Down
4 changes: 3 additions & 1 deletion CoVAR-app/src/app/(pages)/lounge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ const Lounge: React.FC = () => {

// Function to get the current user and check their role
const checkUserStatus = useCallback(async () => {
console.log("getting UNAUTH");
try {
const response = await axios.post(
'/api/getUser',
'/api/getMeoutthefuckingSidebar',
{ accessToken: localStorage.getItem('accessToken') },
{ headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } }
);
Expand All @@ -41,6 +42,7 @@ const Lounge: React.FC = () => {
console.log("Role:", role);

if (role !== 'unauthorised') {
console.log("NOT UNAUTH")
router.replace('/dashboard');
}

Expand Down
5 changes: 3 additions & 2 deletions CoVAR-app/src/app/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { iconStyles, logoStyles, logoutButtonStyles, sidebarItemStyles, sidebarS
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
import { useRouter, usePathname } from 'next/navigation';
import Link from 'next/link';
import { getUserRole } from '@/functions/requests';
import { getAnyUserRole } from '@/functions/requests';
import { doSignOut } from '../functions/firebase/auth';
import HelpDialog from './(pages)/help/helpDialog';
import { Switch } from '@mui/material';
Expand Down Expand Up @@ -48,7 +48,8 @@ const Sidebar: React.FC = () => {
try {
const accessToken = localStorage.getItem('accessToken');
if (accessToken) {
const data = await getUserRole(accessToken);
console.log("IM IN THE FUCKING SIDEBAR");
const data = await getAnyUserRole(accessToken);
setRole(data.role);
}
} catch (error:any) {
Expand Down
9 changes: 9 additions & 0 deletions CoVAR-app/src/functions/requests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ export const getUserRole = async (accessToken: string) => {
return await handleRequest(request);
};

export const getAnyUserRole = async (accessToken: string) => {
const request = {
method: 'post',
url: '/api/getMeoutthefuckingSidebar',
data: { accessToken },
headers: { Authorization: `Bearer ${accessToken}` },
};
return await handleRequest(request);
};
export const fetchUsersByOrg = async (orgId: string, accessToken: string) => {
const request = {
method: 'post',
Expand Down
117 changes: 110 additions & 7 deletions server/lib/securityFunctions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const jwt = require('jsonwebtoken');
const pgClient = require('../lib/postgres');
const fs = require('fs');

const { isVA , isClient ,isAdmin,isUnauthorised} = require('../lib/serverHelperFunctions');
const privateKey = fs.readFileSync('private.pem', 'utf8');
const publicKey = fs.readFileSync('public.pem', 'utf8');
const refreshPrivateKey = fs.readFileSync('refreshPrivate.pem', 'utf8');
Expand All @@ -18,22 +19,122 @@ function verifyToken(token) {
return jwt.verify(token, publicKey, { algorithms: ['RS256'] });
}

function authenticateToken(req, res, next) {
async function authenticateAllToken(req, res, next) {
const authHeader = req.headers['authorization'];
console.log(authHeader);
const token = authHeader && authHeader.split(' ')[1];
if (token == null) return res.sendStatus(401);

if (token == null) return res.sendStatus(401); // No token, unauthorized

jwt.verify(token, publicKey, { algorithms: ['RS256'] }, (err, user) => {
jwt.verify(token, publicKey, { algorithms: ['RS256'] }, async (err, user) => {
if (err) {
console.error('Token verification error:', err);
console.log('Token:', token);
return res.sendStatus(403);
return res.sendStatus(403); // Token invalid, forbidden
}

req.user = user; // Store the user info in the request
const userId = req.user.user_id;

try {
// Await all the role checks
const VAResult = await isVA(pgClient, userId);
const clientResult = await isClient(pgClient, userId);
const adminResult = await isAdmin(pgClient, userId);
const UnauthResult = await isUnauthorised(pgClient, userId);
console.log("UNAUTH RESULT ",UnauthResult.isUnauthorised);
console.log("VA Result:", VAResult.isVA);
console.log("Client Result:", clientResult.isClient);
console.log("Admin Result:", adminResult.isAdmin);
// Check if the user is a VA, client, or admin
if (VAResult.isVA || clientResult.isClient || adminResult.isAdmin || UnauthResult.isUnauthorised) {
// If any of the checks pass, continue to the next middleware or route
return next();
} else {
// If none of the checks pass, send a 403 Forbidden response
return res.status(403).send('Not authorized');
}
} catch (error) {
console.error('Error during role checks:', error);
return res.status(500).send('Server Error'); // Handle any errors from the role checks
}
req.user = user;
next();
});
}
async function authenticateWhiteList(req, res, next){
const authHeader = req.headers['authorization'];
console.log(authHeader);
const token = authHeader && authHeader.split(' ')[1];

if (token == null) return res.sendStatus(401); // No token, unauthorized

jwt.verify(token, publicKey, { algorithms: ['RS256'] }, async (err, user) => {
if (err) {
console.error('Token verification error:', err);
console.log('Token:', token);
return res.sendStatus(403); // Token invalid, forbidden
}

req.user = user; // Store the user info in the request
const userId = req.user.user_id;

try {
// Await all the role checks
const UnauthResult = await isUnauthorised(pgClient, userId);
// Check if the user is a VA, client, or admin
console.log("UNAUTH RESULT ",UnauthResult.isUnauthorised);
if (UnauthResult.isUnauthorised ) {
// If any of the checks pass, continue to the next middleware or route
return next();
} else {
// If none of the checks pass, send a 403 Forbidden response
return res.status(403).send('Not authorized');
}
} catch (error) {
console.error('Error during role checks:', error);
return res.status(500).send('Server Error'); // Handle any errors from the role checks
}
});
}
async function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
console.log(authHeader);
const token = authHeader && authHeader.split(' ')[1];

if (token == null) return res.sendStatus(401); // No token, unauthorized

jwt.verify(token, publicKey, { algorithms: ['RS256'] }, async (err, user) => {
if (err) {
console.error('Token verification error:', err);
console.log('Token:', token);
return res.sendStatus(403); // Token invalid, forbidden
}

req.user = user; // Store the user info in the request
const userId = req.user.user_id;

try {
// Await all the role checks
const VAResult = await isVA(pgClient, userId);
const clientResult = await isClient(pgClient, userId);
const adminResult = await isAdmin(pgClient, userId);
console.log("VA Result:", VAResult.isVA);
console.log("Client Result:", clientResult.isClient);
console.log("Admin Result:", adminResult.isAdmin);
// Check if the user is a VA, client, or admin
if (VAResult.isVA || clientResult.isClient || adminResult.isAdmin) {
// If any of the checks pass, continue to the next middleware or route
return next();
} else {
// If none of the checks pass, send a 403 Forbidden response
return res.status(403).send('Not authorized');
}
} catch (error) {
console.error('Error during role checks:', error);
return res.status(500).send('Server Error'); // Handle any errors from the role checks
}
});
}


// Firebase sdk
const admin = require('firebase-admin');
Expand All @@ -57,6 +158,8 @@ async function verifyIdToken(req,res,next) {
}

module.exports = {
authenticateAllToken,
authenticateWhiteList,
generateToken,
generateRefreshToken,
verifyToken,
Expand Down
Loading
Loading