From e30681705f827aad31f217603f9c14a1f528fc00 Mon Sep 17 00:00:00 2001 From: MathieuSevignyLavallee <89943988+MathieuSevignyLavallee@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:13:16 -0400 Subject: [PATCH] Roles done --- client/src/pages/AuthManager/AuthDrawer.tsx | 1 - .../AuthManager/callback/AuthCallback.tsx | 9 -- .../providers/OAuth-Oidc/ButtonAuth.tsx | 3 +- .../providers/SimpleLogin/Login.tsx | 10 +- .../providers/SimpleLogin/Register.tsx | 5 - client/src/services/ApiService.tsx | 100 ++++++++++------- client/src/services/AuthService.tsx | 1 - docker-compose.yaml | 2 - server/auth/auth-manager.js | 4 +- .../auth/modules/passport-providers/oauth.js | 104 +++++++++--------- .../auth/modules/passport-providers/oidc.js | 87 +++++++-------- server/auth/modules/simpleauth.js | 80 +++++++------- server/middleware/jwtToken.js | 8 +- server/models/users.js | 36 +++--- 14 files changed, 227 insertions(+), 223 deletions(-) diff --git a/client/src/pages/AuthManager/AuthDrawer.tsx b/client/src/pages/AuthManager/AuthDrawer.tsx index db01db4..093b7aa 100644 --- a/client/src/pages/AuthManager/AuthDrawer.tsx +++ b/client/src/pages/AuthManager/AuthDrawer.tsx @@ -15,7 +15,6 @@ const AuthSelection: React.FC = () => { useEffect(() => { const fetchData = async () => { const data = await authService.fetchAuthData(); - console.log(data); setAuthData(data); }; diff --git a/client/src/pages/AuthManager/callback/AuthCallback.tsx b/client/src/pages/AuthManager/callback/AuthCallback.tsx index c9b4997..6ba290d 100644 --- a/client/src/pages/AuthManager/callback/AuthCallback.tsx +++ b/client/src/pages/AuthManager/callback/AuthCallback.tsx @@ -1,7 +1,6 @@ import { useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import apiService from '../../../services/ApiService'; -import { jwtDecode } from 'jwt-decode'; const OAuthCallback: React.FC = () => { const navigate = useNavigate(); @@ -12,15 +11,7 @@ const OAuthCallback: React.FC = () => { const user = searchParams.get('user'); if (user) { - // Save user data to localStorage or sessionStorage - console.log(user); apiService.saveToken(user); - - const decodedToken = jwtDecode(user); - const { email } = decodedToken as { email: string;}; - console.log(email + " connected!"); - - // Navigate to the dashboard or another page navigate('/'); } else { navigate('/login'); diff --git a/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx b/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx index b561217..8c3fe57 100644 --- a/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx +++ b/client/src/pages/AuthManager/providers/OAuth-Oidc/ButtonAuth.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { ENV_VARIABLES } from '../../../../constants'; import '../css/buttonAuth.css'; interface ButtonAuthContainerProps { @@ -8,7 +7,7 @@ interface ButtonAuthContainerProps { } const handleAuthLogin = (provider: string) => { - window.location.href = `${ENV_VARIABLES.VITE_BACKEND_URL}/api/auth/` + provider; + window.location.href = `/api/auth/` + provider; }; const ButtonAuth: React.FC = ({ providerName, providerType }) => { diff --git a/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx b/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx index 559858f..6356d7a 100644 --- a/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx +++ b/client/src/pages/AuthManager/providers/SimpleLogin/Login.tsx @@ -1,4 +1,4 @@ -import { useNavigate, Link } from 'react-router-dom'; +import { Link } from 'react-router-dom'; // JoinRoom.tsx import React, { useEffect, useState } from 'react'; @@ -11,7 +11,6 @@ import LoginContainer from '../../../../components/LoginContainer/LoginContainer import ApiService from '../../../../services/ApiService'; const SimpleLogin: React.FC = () => { - const navigate = useNavigate(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -27,15 +26,10 @@ const SimpleLogin: React.FC = () => { const login = async () => { const result = await ApiService.login(email, password); - - if (result != true) { + if (result !== true) { setConnectionError(result); return; } - else { - navigate("/") - } - }; diff --git a/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx b/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx index 02be56e..d33527d 100644 --- a/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx +++ b/client/src/pages/AuthManager/providers/SimpleLogin/Register.tsx @@ -1,5 +1,3 @@ -import { useNavigate } from 'react-router-dom'; - // JoinRoom.tsx import React, { useEffect, useState } from 'react'; @@ -10,7 +8,6 @@ import LoginContainer from '../../../../components/LoginContainer/LoginContainer import ApiService from '../../../../services/ApiService'; const Register: React.FC = () => { - const navigate = useNavigate(); const [name, setName] = useState(''); // State for name const [email, setEmail] = useState(''); @@ -46,8 +43,6 @@ const Register: React.FC = () => { setConnectionError(result); return; } - - navigate("/login"); }; return ( diff --git a/client/src/services/ApiService.tsx b/client/src/services/ApiService.tsx index 9e48d9a..d5c0b28 100644 --- a/client/src/services/ApiService.tsx +++ b/client/src/services/ApiService.tsx @@ -1,5 +1,5 @@ import axios, { AxiosError, AxiosResponse } from 'axios'; -import {jwtDecode} from 'jwt-decode'; +import { jwtDecode } from 'jwt-decode'; import { ENV_VARIABLES } from '../constants'; import { QuizType } from '../Types/QuizType'; @@ -79,26 +79,25 @@ class ApiService { public isLoggedInTeacher(): boolean { const token = this.getToken(); - - console.log("Check if loggedIn : " + token); - + + if (token == null) { return false; } - + try { const decodedToken = jwtDecode(token) as { roles: string[] }; - + const userRoles = decodedToken.roles; const requiredRole = 'teacher'; - + if (!userRoles || !userRoles.includes(requiredRole)) { return false; } - + // Update token expiry this.saveToken(token); - + return true; } catch (error) { console.error("Error decoding token:", error); @@ -129,8 +128,12 @@ class ApiService { const result: AxiosResponse = await axios.post(url, body, { headers: headers }); - if (result.status !== 200) { - throw new Error(`L'enregistrement a échoué. Status: ${result.status}`); + console.log(result); + if (result.status == 200) { + window.location.href = result.request.responseURL; + } + else { + throw new Error(`La connexion a échoué. Status: ${result.status}`); } return true; @@ -152,39 +155,52 @@ class ApiService { * @returns true if successful * @returns A error string if unsuccessful, */ - public async login(email: string, password: string): Promise { - try { - - if (!email || !password) { - throw new Error(`L'email et le mot de passe sont requis.`); - } - - const url: string = this.constructRequestUrl(`/auth/simple-auth/login`); - const headers = this.constructRequestHeaders(); - const body = { email, password }; - - const result: AxiosResponse = await axios.post(url, body, { headers: headers }); - - if (result.status !== 200) { - throw new Error(`La connexion a échoué. Status: ${result.status}`); - } - - this.saveToken(result.data.token); - - return true; - - } catch (error) { - console.log("Error details: ", error); - - if (axios.isAxiosError(error)) { - const err = error as AxiosError; - const data = err.response?.data as { error: string } | undefined; - return data?.error || 'Erreur serveur inconnue lors de la requête.'; - } - - return `Une erreur inattendue s'est produite.` + /** + * @returns true if successful + * @returns An error string if unsuccessful + */ +public async login(email: string, password: string): Promise { + try { + if (!email || !password) { + throw new Error("L'email et le mot de passe sont requis."); } + + const url: string = this.constructRequestUrl(`/auth/simple-auth/login`); + const headers = this.constructRequestHeaders(); + const body = { email, password }; + + const result: AxiosResponse = await axios.post(url, body, { headers: headers }); + + // If login is successful, redirect the user + if (result.status === 200) { + window.location.href = result.request.responseURL; + return true; + } else { + throw new Error(`La connexion a échoué. Statut: ${result.status}`); + } + } catch (error) { + console.log("Error details:", error); + + // Handle Axios-specific errors + if (axios.isAxiosError(error)) { + const err = error as AxiosError; + const responseData = err.response?.data as { message?: string } | undefined; + + // If there is a message field in the response, print it + if (responseData?.message) { + console.log("Backend error message:", responseData.message); + return responseData.message; + } + + // If no message is found, return a fallback message + return "Erreur serveur inconnue lors de la requête."; + } + + // Handle other non-Axios errors + return "Une erreur inattendue s'est produite."; } +} + /** * @returns true if successful diff --git a/client/src/services/AuthService.tsx b/client/src/services/AuthService.tsx index e3f2494..bca616d 100644 --- a/client/src/services/AuthService.tsx +++ b/client/src/services/AuthService.tsx @@ -15,7 +15,6 @@ class AuthService { async fetchAuthData(){ try { const response = await fetch(this.constructRequestUrl('/auth/getActiveAuth')); - console.log("base url: " + this.BASE_URL); const data = await response.json(); return data.authActive; } catch (error) { diff --git a/docker-compose.yaml b/docker-compose.yaml index 91905b1..898a78a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,8 +7,6 @@ services: context: ./client dockerfile: Dockerfile container_name: frontend - environment: - VITE_BACKEND_URL: http://localhost:3000 ports: - "5173:5173" restart: always diff --git a/server/auth/auth-manager.js b/server/auth/auth-manager.js index bc16fb9..fce031e 100644 --- a/server/auth/auth-manager.js +++ b/server/auth/auth-manager.js @@ -43,8 +43,8 @@ class AuthManager{ } async login(userInfo,req,res,next){ - const tokenToSave = jwt.create(userInfo.email, userInfo._id); - res.redirect(`${process.env['FRONTEND_URL']}/auth/callback?user=${tokenToSave}`); + const tokenToSave = jwt.create(userInfo.email, userInfo._id,userInfo.roles); + res.redirect(`/auth/callback?user=${tokenToSave}`); console.info(`L'utilisateur '${userInfo.name}' vient de se connecter`) } diff --git a/server/auth/modules/passport-providers/oauth.js b/server/auth/modules/passport-providers/oauth.js index 5730eb5..188d8a6 100644 --- a/server/auth/modules/passport-providers/oauth.js +++ b/server/auth/modules/passport-providers/oauth.js @@ -5,13 +5,13 @@ var { hasNestedValue } = require('../../../utils') var jwt = require('../../../middleware/jwtToken') class PassportOAuth { - constructor(passportjs,auth_name){ + constructor(passportjs, auth_name) { this.passportjs = passportjs this.auth_name = auth_name } - register(app, passport,endpoint, name, provider) { - const cb_url =`${process.env['BACKEND_URL']}${endpoint}/${name}/callback` + register(app, passport, endpoint, name, provider) { + const cb_url = `${process.env['BACKEND_URL']}${endpoint}/${name}/callback` const self = this passport.use(name, new OAuth2Strategy({ @@ -22,72 +22,72 @@ class PassportOAuth { callbackURL: cb_url, passReqToCallback: true }, - async function(req, accessToken, refreshToken, params, profile, done) { - try { - const userInfoResponse = await fetch(provider.OAUTH_USERINFO_URL, { - headers: { 'Authorization': `Bearer ${accessToken}` } - }); - const userInfo = await userInfoResponse.json(); + async function (req, accessToken, refreshToken, params, profile, done) { + try { + const userInfoResponse = await fetch(provider.OAUTH_USERINFO_URL, { + headers: { 'Authorization': `Bearer ${accessToken}` } + }); + const userInfo = await userInfoResponse.json(); - let received_user = { - auth_id: userInfo.sub, - email: userInfo.email, - name: userInfo.name, - roles: [] - }; - - if(hasNestedValue(userInfo,provider.OAUTH_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher') - if(hasNestedValue(userInfo,provider.OAUTH_ROLE_STUDENT_VALUE)) received_user.roles.push('student') + let received_user = { + auth_id: userInfo.sub, + email: userInfo.email, + name: userInfo.name, + roles: [] + }; - const user_association = await authUserAssoc.find_user_association(self.auth_name,received_user.auth_id) + if (hasNestedValue(userInfo, provider.OAUTH_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher') + if (hasNestedValue(userInfo, provider.OAUTH_ROLE_STUDENT_VALUE)) received_user.roles.push('student') - let user_account - if(user_association){ - user_account = await users.getById(user_association.user_id) - } - else { - let user_id = await users.getId(received_user.email) - if(user_id){ - user_account = await users.getById(user_id); - } else { - received_user.password = users.generatePassword() - user_account = await self.passportjs.register(received_user) + const user_association = await authUserAssoc.find_user_association(self.auth_name, received_user.auth_id) + + let user_account + if (user_association) { + user_account = await users.getById(user_association.user_id) } - await authUserAssoc.link(self.auth_name,received_user.auth_id,user_account._id) + else { + let user_id = await users.getId(received_user.email) + if (user_id) { + user_account = await users.getById(user_id); + } else { + received_user.password = users.generatePassword() + user_account = await self.passportjs.register(received_user) + } + await authUserAssoc.link(self.auth_name, received_user.auth_id, user_account._id) + } + + user_account.name = received_user.name + user_account.roles = received_user.roles + await users.editUser(user_account) + + // Store the tokens in the session + req.session.oauth2Tokens = { + accessToken: accessToken, + refreshToken: refreshToken, + expiresIn: params.expires_in + }; + + return done(null, user_account); + } catch (error) { + console.error(`Erreur dans la strategie OAuth2 '${name}' : ${error}`); + return done(error); } - - user_account.name = received_user.name - user_account.roles = received_user.roles - await users.editUser(user_account) - - // Store the tokens in the session - req.session.oauth2Tokens = { - accessToken: accessToken, - refreshToken: refreshToken, - expiresIn: params.expires_in - }; - - return done(null, user_account); - } catch (error) { - console.error(`Erreur dans la strategie OAuth2 '${name}' : ${error}`); - return done(error); - } - })); + })); app.get(`${endpoint}/${name}`, (req, res, next) => { passport.authenticate(name, { - scope: 'openid profile email offline_access'+ ` ${provider.OAUTH_ADD_SCOPE}`, + scope: 'openid profile email offline_access' + ` ${provider.OAUTH_ADD_SCOPE}`, prompt: 'consent' })(req, res, next); }); - app.get(`${endpoint}/${name}/callback`, + app.get(`${endpoint}/${name}/callback`, (req, res, next) => { passport.authenticate(name, { failureRedirect: '/login' })(req, res, next); }, (req, res) => { if (req.user) { - self.passportjs.authenticate(req.user,req,res) + self.passportjs.authenticate(req.user, req, res) } else { res.status(401).json({ error: "L'authentification a échoué" }); } diff --git a/server/auth/modules/passport-providers/oidc.js b/server/auth/modules/passport-providers/oidc.js index 65997b9..019ccac 100644 --- a/server/auth/modules/passport-providers/oidc.js +++ b/server/auth/modules/passport-providers/oidc.js @@ -5,13 +5,13 @@ var { hasNestedValue } = require('../../../utils') var jwt = require('../../../middleware/jwtToken') class PassportOpenIDConnect { - constructor(passportjs,auth_name){ + constructor(passportjs, auth_name) { this.passportjs = passportjs this.auth_name = auth_name } - async getConfigFromConfigURL(name,provider){ - try{ + async getConfigFromConfigURL(name, provider) { + try { const config = await fetch(provider.OIDC_CONFIG_URL) return await config.json() } catch (error) { @@ -19,10 +19,10 @@ class PassportOpenIDConnect { } } - async register(app, passport,endpoint, name, provider) { + async register(app, passport, endpoint, name, provider) { - const config = await this.getConfigFromConfigURL(name,provider) - const cb_url =`${process.env['BACKEND_URL']}${endpoint}/${name}/callback` + const config = await this.getConfigFromConfigURL(name, provider) + const cb_url = `${process.env['BACKEND_URL']}${endpoint}/${name}/callback` const self = this passport.use(name, new OpenIDConnectStrategy({ @@ -36,59 +36,60 @@ class PassportOpenIDConnect { passReqToCallback: true, scope: 'openid profile email ' + `${provider.OIDC_ADD_SCOPE}`, }, - // patch pour la librairie permet d'obtenir les groupes, PR en cours mais "morte" : https://github.com/jaredhanson/passport-openidconnect/pull/101 - async function(req, issuer, profile, times, tok, done) { - try { - const received_user = { - auth_id: profile.id, - email: profile.emails[0].value, - name: profile.name.givenName, - roles: [] - }; + // patch pour la librairie permet d'obtenir les groupes, PR en cours mais "morte" : https://github.com/jaredhanson/passport-openidconnect/pull/101 + async function (req, issuer, profile, times, tok, done) { + try { + const received_user = { + auth_id: profile.id, + email: profile.emails[0].value, + name: profile.name.givenName, + roles: [] + }; - if(hasNestedValue(profile,provider.OIDC_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher') - if(hasNestedValue(profile,provider.OIDC_ROLE_STUDENT_VALUE)) received_user.roles.push('student') - - const user_association = await authUserAssoc.find_user_association(self.auth_name,received_user.auth_id) - let user_account - if(user_association){ - user_account = await users.getById(user_association.user_id) - } - else { - let user_id = await users.getId(received_user.email) - if(user_id){ - user_account = await users.getById(user_id); - } else { - received_user.password = users.generatePassword() - user_account = await self.passportjs.register(received_user) + if (hasNestedValue(profile, provider.OIDC_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher') + if (hasNestedValue(profile, provider.OIDC_ROLE_STUDENT_VALUE)) received_user.roles.push('student') + + const user_association = await authUserAssoc.find_user_association(self.auth_name, received_user.auth_id) + + let user_account + if (user_association) { + user_account = await users.getById(user_association.user_id) } - await authUserAssoc.link(self.auth_name,received_user.auth_id,user_account._id) + else { + let user_id = await users.getId(received_user.email) + if (user_id) { + user_account = await users.getById(user_id); + } else { + received_user.password = users.generatePassword() + user_account = await self.passportjs.register(received_user) + } + await authUserAssoc.link(self.auth_name, received_user.auth_id, user_account._id) + } + + user_account.name = received_user.name + user_account.roles = received_user.roles + await users.editUser(user_account) + + return done(null, user_account); + } catch (error) { } - - user_account.name = received_user.name - user_account.roles = received_user.roles - await users.editUser(user_account) - - return done(null, user_account); - } catch (error) { - } - })); + })); app.get(`${endpoint}/${name}`, (req, res, next) => { passport.authenticate(name, { - scope: 'openid profile email offline_access'+ ` ${provider.OAUTH_ADD_SCOPE}`, + scope: 'openid profile email offline_access' + ` ${provider.OAUTH_ADD_SCOPE}`, prompt: 'consent' })(req, res, next); }); - app.get(`${endpoint}/${name}/callback`, + app.get(`${endpoint}/${name}/callback`, (req, res, next) => { passport.authenticate(name, { failureRedirect: '/login' })(req, res, next); }, (req, res) => { if (req.user) { - self.passportjs.authenticate(req.user,req,res) + self.passportjs.authenticate(req.user, req, res) } else { res.status(401).json({ error: "L'authentification a échoué" }); } diff --git a/server/auth/modules/simpleauth.js b/server/auth/modules/simpleauth.js index 4e048db..abdf537 100644 --- a/server/auth/modules/simpleauth.js +++ b/server/auth/modules/simpleauth.js @@ -6,52 +6,34 @@ const AppError = require('../../middleware/AppError.js'); const { MISSING_REQUIRED_PARAMETER, LOGIN_CREDENTIALS_ERROR, GENERATE_PASSWORD_ERROR, UPDATE_PASSWORD_ERROR } = require('../../constants/errorCodes'); const { name } = require('../../models/authProvider.js'); -class SimpleAuth{ - constructor(authmanager,settings){ +class SimpleAuth { + constructor(authmanager, settings) { this.authmanager = authmanager this.providers = settings this.endpoint = "/api/auth/simple-auth" } - async registerAuth(expressapp){ - try{ - expressapp.post(`${this.endpoint}/register`, (req,res,next)=>this.register(this,req,res)); - expressapp.post(`${this.endpoint}/login`, (req,res,next)=>this.authenticate(this,req,res)); - expressapp.post(`${this.endpoint}/reset-password`, (req,res,next)=>this.resetPassword(this,req,res)); - expressapp.post(`${this.endpoint}/change-password`, jwt.authenticate, (req,res,next)=>this.changePassword(this,req,res)); - } catch(error){ + async registerAuth(expressapp) { + try { + expressapp.post(`${this.endpoint}/register`, (req, res, next) => this.register(this, req, res)); + expressapp.post(`${this.endpoint}/login`, (req, res, next) => this.authenticate(this, req, res)); + expressapp.post(`${this.endpoint}/reset-password`, (req, res, next) => this.resetPassword(this, req, res)); + expressapp.post(`${this.endpoint}/change-password`, jwt.authenticate, (req, res, next) => this.changePassword(this, req, res)); + } catch (error) { console.error(`La connexion ${name} de type ${provider.type} n'as pu être chargé.`) } } - async register(self,req, res) { - let userInfos = { - name: req.body.name, - email: req.body.email, - password: req.body.password, - roles: req.body.roles - } - let user = await self.authmanager.register(userInfos) - if(user) res.redirect("/") - else res.redirect("/login") - } - - async authenticate(self,req, res, next) { + async register(self, req, res) { try { - const { email, password } = req.body; - - if (!email || !password) { - throw new AppError(MISSING_REQUIRED_PARAMETER); + let userInfos = { + name: req.body.name, + email: req.body.email, + password: req.body.password, + roles: req.body.roles } - - const user = await model.login(email, password); - - if (!user) { - throw new AppError(LOGIN_CREDENTIALS_ERROR); - } - - user.name = user.name ?? user.email - self.authmanager.login(user,req,res,next) + let user = await self.authmanager.register(userInfos) + if (user) res.redirect("/login") } catch (error) { return res.status(400).json({ @@ -60,7 +42,29 @@ class SimpleAuth{ } } - async resetPassword(self,req, res, next) { + async authenticate(self, req, res, next) { + try { + const { email, password } = req.body; + + if (!email || !password) { + const error = new Error("Email or password is missing"); + error.statusCode = 400; + throw error; + } + + const user = await model.login(email, password); + + await self.authmanager.login(user, req, res, next); + } catch (error) { + const statusCode = error.statusCode || 500; + const message = error.message || "An internal server error occurred"; + + console.error(error); + return res.status(statusCode).json({ message }); + } + } + + async resetPassword(self, req, res, next) { try { const { email } = req.body; @@ -85,7 +89,7 @@ class SimpleAuth{ } } - async changePassword(self,req, res, next) { + async changePassword(self, req, res, next) { try { const { email, oldPassword, newPassword } = req.body; @@ -114,7 +118,7 @@ class SimpleAuth{ return next(error); } } - + } module.exports = SimpleAuth; \ No newline at end of file diff --git a/server/middleware/jwtToken.js b/server/middleware/jwtToken.js index 292e591..75ad458 100644 --- a/server/middleware/jwtToken.js +++ b/server/middleware/jwtToken.js @@ -7,8 +7,8 @@ dotenv.config(); class Token { - create(email, userId) { - return jwt.sign({ email, userId }, process.env.JWT_SECRET); + create(email, userId, roles) { + return jwt.sign({ email, userId, roles }, process.env.JWT_SECRET); } authenticate(req, res, next) { @@ -25,11 +25,11 @@ class Token { req.user = payload; }); - + } catch (error) { return next(error); } - + return next(); } } diff --git a/server/models/users.js b/server/models/users.js index c016c0a..a392913 100644 --- a/server/models/users.js +++ b/server/models/users.js @@ -64,24 +64,32 @@ class Users { } async login(email, password) { - await db.connect(); - const conn = db.getConnection(); + try { + await db.connect(); + const conn = db.getConnection(); + const userCollection = conn.collection("users"); - const userCollection = conn.collection("users"); + const user = await userCollection.findOne({ email: email }); - const user = await userCollection.findOne({ email: email }); + if (!user) { + const error = new Error("User not found"); + error.statusCode = 404; + throw error; + } - if (!user) { - return false; + const passwordMatch = await this.verify(password, user.password); + + if (!passwordMatch) { + const error = new Error("Password does not match"); + error.statusCode = 401; + throw error; + } + + return user; + } catch (error) { + console.error(error); + throw error; } - - const passwordMatch = await this.verify(password, user.password); - - if (!passwordMatch) { - return false; - } - - return user; } async resetPassword(email) {