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

Feature/authentication #158

Merged
merged 12 commits into from
Oct 12, 2024
Merged
10 changes: 5 additions & 5 deletions backend/config/databaseConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ const mysql = require('mysql2');
require('dotenv').config();

const connection = mysql.createConnection({
host: process.env.DOCKER_USE_MYSQL_HOST ? "database" : process.env.DB_HOST, // docker compose service name which resolves to internal IP (by docker)
user: process.env.DB_USER,
password: process.env.MYSQL_ROOT_PASSWORD,
database: process.env.DB,
port: process.env.MYSQL_TCP_PORT,
host: process.env.MYSQLHOST, // docker compose service name which resolves to internal IP (by docker)
user: process.env.MYSQLUSER,
password: process.env.MYSQLPASSWORD,
database: process.env.MYSQL_DATABASE,
port: process.env.MYSQLPORT,
});

// const connection = mysql.createPool({
Expand Down
1 change: 0 additions & 1 deletion backend/helpers/formsHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const { v4: uuidv4 } = require('uuid');
const { MAX_ACCESS_BOUNDARY_RULES_COUNT } = require('google-auth-library/build/src/auth/downscopedclient');
require('dotenv').config();``

console.log(process.env)
const SERVICE_ACCOUNT_JSON = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_TOKEN )
const SCOPES = ['https://www.googleapis.com/auth/forms', 'https://www.googleapis.com/auth/drive'];
const auth = new GoogleAuth({
Expand Down
15 changes: 8 additions & 7 deletions backend/middleware/auth0Middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ const axios = require('axios');

async function getUserDetails(req) {
const accessToken = req.auth.token;
const userResponse = await axios.get(process.env.AUTH_DOMAIN+ 'userinfo',
const userResponse = await axios.get(process.env.AUTH_DOMAIN+ '/userinfo',
{
headers: {
authorization: `Bearer ${accessToken}`
}
})

return userResponse.data;
}

Expand All @@ -21,14 +20,16 @@ async function delay(time) {


const auth0Middleware = (app) => {
const verifyJwt = auth({
audience: 'balance-api-endpoint',
issuerBaseURL: process.env.AUTH_DOMAIN,
const jwtCheck = auth({
audience: 'https://balance-api-endpoint',
issuerBaseURL: 'https://balancedev.au.auth0.com/',
tokenSigningAlg: 'RS256'
});

app.use(verifyJwt);

app.use(jwtCheck, (req, res, next) => {
next();
});

app.use(async (req, res, next) => {
try {
req.user = await getUserDetails(req);
Expand Down
45 changes: 33 additions & 12 deletions backend/routeHandlers/unitRouteHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,26 @@ const getAllUnits = async (req, res) => {
/**
* Gets all units registered under a user
*/
const email = req.user.email;
try {
const units = await promiseBasedQuery("SELECT * FROM unit_offering;");
const [staff] = await promiseBasedQuery(
"SELECT staff_unique_id FROM staff WHERE email_address = ?;",
[email]
);

if (!staff) {
return res.status(404).send('Staff not found');
}
staff_id= staff.staff_unique_id
console.log('getreq: ',staff_id)
const units = await promiseBasedQuery("SELECT * FROM unit_offering where staff_unique_id=?",[staff_id]);
res.status(200).json(units);
} catch (e) {
console.log(e);
}
};

const addUnit = async (req, res) => {
/**
* Adds a new unit under a users account
*/
const {
unitCode,
unitName,
Expand All @@ -30,13 +38,27 @@ const addUnit = async (req, res) => {
color
} = req.body;

const email = req.user.email; // Assuming email is available from the decoded token

try {
// note: unique id has auto_increment enabled thus not provided
// First, retrieve staff_id from the staff table
const [staff] = await promiseBasedQuery(
"SELECT staff_unique_id FROM staff WHERE email_address = ?;",
[email]
);

if (!staff) {
return res.status(404).send('Staff not found');
}
staff_id= staff.staff_unique_id
console.log(staff_id)

// Now, insert the unit offering
await promiseBasedQuery(
"INSERT INTO unit_offering " +
"(unit_code, unit_name, unit_off_year, unit_off_period, enrolment_count, unit_color) " +
"VALUES (?, ?, ?, ?, ?, ?);",
[unitCode, unitName, Number(year), period, 0, color]
"(unit_code, unit_name, unit_off_year, unit_off_period, staff_unique_id, enrolment_count, unit_color) " +
"VALUES (?, ?, ?, ?, ?, ?, ?);",
[unitCode, unitName, Number(year), period, staff_id, 0, color]
);

res.status(200).send();
Expand All @@ -45,18 +67,17 @@ const addUnit = async (req, res) => {
let errorMsg;
switch (error.code) {
case "ER_DUP_ENTRY":
errorMsg = `the unit offering ${unitCode}, ${year}, ${period} already exists`;
errorMsg = `The unit offering ${unitCode}, ${year}, ${period} already exists`;
break;
case "ER_BAD_FIELD_ERROR":
errorMsg = `year ${year} is not a number`;
errorMsg = `Year ${year} is not a number`;
break;
default:
errorMsg = "something went wrong :(";
errorMsg = "Something went wrong :(";
}
res.status(400).send(errorMsg);
}
};

const deleteUnit = async function (req, res) {
/**
* Deletes a unit registered under a user by going through related foreign key constraints
Expand Down
41 changes: 30 additions & 11 deletions backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,43 @@ const app = express();
// required to attach reqs with a body to the req object for req handling
app.use(express.json());
app.use(cors(corsOptions));
app.use((req, res, next) => {
console.log(req.path, req.method);
next();
});


if (process.env.AUTH == "TEST") { auth0Middleware(app); }

if (process.env.AUTH == "DEV" || process.env.AUTH == null) { mockAuthMiddleware(app); }



app.use(async (req, res, next) => {
results = await db_connection.promise().query(`SELECT * FROM staff WHERE email_address='${req.user.email}';`);
if (results[0].length === 0) {
await db_connection.promise().query(
`INSERT INTO staff (preferred_name, last_name, email_address)
VALUES ('${req.user.nickname}', '${req.user.nickname}', '${req.user.email}');`
)
try {
if (req.user && req.user.email) {
const results = await db_connection.promise().query(
`SELECT * FROM staff WHERE email_address='${req.user.email}';`
);

if (results[0].length === 0) {
await db_connection.promise().query(
`INSERT INTO staff (preferred_name, last_name, email_address)
VALUES ('${req.user.nickname}', '${req.user.nickname}', '${req.user.email}');`
);
}
}
next();
} catch (error) {
console.error('Error updating staff info:', error);
res.status(500).send('Server error');
}
});
app.use((req, res, next) => {
console.log(req.path, req.method);
if (req.user) {
console.log('Authenticated user');
} else {
console.log('Unauthenticated request');
}
next();
})
});

app.use('/api/units/', unitRoutes);
app.use('/api/groups/', groupRoutes);
Expand Down
6 changes: 3 additions & 3 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VITE_REACT_APP_AUTH="DEV"
VITE_REACT_AUTH_DOMAIN=""
VITE_REACT_APP_AUTH_CLIENT_ID=""
VITE_REACT_APP_AUTH="TEST"
VITE_REACT_AUTH_DOMAIN="balancedev.au.auth0.com"
VITE_REACT_APP_AUTH_CLIENT_ID="dT92M5EBX8hoE1XmYPe8VuEK9ltmUf1e"
29 changes: 6 additions & 23 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import GroupAnalytics from './pages/GroupAnalytics';
import Import from './pages/Import';
import RegistrationPage from "./components/loginPage/RegistrationPage.jsx";
import FAQ from './pages/FAQ';
import UserDashboard from './pages/UserDashboard.jsx';

const theme = extendTheme({
colors: {
Expand Down Expand Up @@ -93,32 +94,14 @@ function App() {
path="/login"
element={<LoginPage />}
/>
<Route
path="/userInfo"
element={<UserDashboard />}
/>
</Routes>
)}
{!isAuthenticated && (
<Container centerContent>
<Routes>
<Route
path="/login"
element={<LoginPage />}
/>
<Route
path="signup"
element={<RegistrationPage />}
/>
</Routes>
<Box height="100px"></Box>
<Heading as="h3" size="xl">
<Link as={RouterLink} to="login" ml="8">
<button>
<Heading as="h2" size="xl" variant="underline">
Login
</Heading>
</button>{' '}
</Link>
to Create Groups!
</Heading>
</Container>
<LandingPage/>
)}
</div>
</BrowserRouter>
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/components/_shared/LoadingSpinner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { Spinner, Flex } from '@chakra-ui/react';

const LoadingSpinner = () => (
<Flex justify="center" align="center" height="100vh">
<Spinner
thickness="4px"
speed="0.65s"
emptyColor="gray.200"
color="blue.500"
size="xl"
/>
</Flex>
);

export default LoadingSpinner;
41 changes: 21 additions & 20 deletions frontend/src/components/_shared/NavBar.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useAuth0 } from '@auth0/auth0-react';
import { Image, Flex, Button, Link } from '@chakra-ui/react';
import { Image, Flex, Button, Link,Menu,MenuButton,MenuList,MenuItem,Avatar, IconButton } from '@chakra-ui/react';
import { Link as RouterLink } from 'react-router-dom';

import { ChevronDownIcon } from '@chakra-ui/icons';
import { MockAuth } from '../../helpers/mockAuth';
import logo from '../../assets/logo_separated.png';
import { useNavigate } from 'react-router-dom';


const NavBar = ({ authenticated }) => {
let authService = {
Expand All @@ -14,12 +14,6 @@ const NavBar = ({ authenticated }) => {

const { loginWithRedirect, logout } =
authService[import.meta.env.VITE_REACT_APP_AUTH]();
const navigate = useNavigate();

const navigateToPage = () =>{
console.log("Going to contributors page")
navigate("/ContributorPage")
}

return (
<Flex as="header"
Expand Down Expand Up @@ -71,17 +65,24 @@ const NavBar = ({ authenticated }) => {
</Link>

</Flex>
<Flex>
{authenticated ? (
<Button onClick={logout} height="8vh" width="14vh" maxHeight="2.6em" minHeight="1.8em" maxWidth="6.5em" minWidth="4.3em" boxShadow="lg">Logout</Button>
) : (
<Link as={RouterLink} to="/login" ml="8">
<Button>
Login
</Button>
</Link>
)}
</Flex>
<Menu>
<MenuButton
as={IconButton}
icon={<Avatar size="sm"/>}
boxShadow="lg"
>
</MenuButton>
<MenuList>
{authenticated ? (
<>
<MenuItem as={RouterLink} to="/userInfo">View Profile</MenuItem>
<MenuItem onClick={logout}>Logout</MenuItem>
</>
) : (
<MenuItem onClick={loginWithRedirect}>Login</MenuItem>
)}
</MenuList>
</Menu>
</Flex>
);
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ root.render(
clientId={import.meta.env.VITE_REACT_APP_AUTH_CLIENT_ID}
authorizationParams={{
redirect_uri: window.location.origin,
audience: 'balance-api-endpoint',
audience: 'https://balance-api-endpoint',
scope: 'openid profile email',
}}
>
Expand Down
31 changes: 21 additions & 10 deletions frontend/src/pages/LandingPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,20 @@ import {
Link
} from '@chakra-ui/react';
import { Link as RouterLink } from 'react-router-dom';

import { useAuth0 } from '@auth0/auth0-react';
import { MockAuth } from '../helpers/mockAuth';
import logo from '../assets/logo.png';
import LoadingSpinner from '../components/_shared/LoadingSpinner';
function LandingPage() {
let authService = {
DEV: MockAuth,
TEST: useAuth0,
};

const { isAuthenticated,isLoading, loginWithRedirect, logout } = authService[import.meta.env.VITE_REACT_APP_AUTH]();
if(isLoading){
return (<LoadingSpinner/>)
}
return (
<Container maxW={'7xl'}>
<Stack
Expand Down Expand Up @@ -71,15 +82,15 @@ function LandingPage() {
Get Started
</Button>
</Link>
<Link as={RouterLink} to="/login" ml="8">
<Button
rounded={'full'}
size={'lg'}
fontWeight={'normal'}
px={6}>
Log In
</Button>
</Link>
<Flex>
{isAuthenticated ? (
<Button onClick={logout} height="8vh" width="14vh" maxHeight="2.6em" minHeight="1.8em" maxWidth="6.5em" minWidth="4.3em" boxShadow="lg">Logout</Button>
) : (
<Button onClick={()=> loginWithRedirect()}>
Login
</Button>
)}
</Flex>
</Stack>
</Stack>
<Flex
Expand Down
Loading
Loading