diff --git a/.env b/.env index 7051eac..34e3e37 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ PORT=8080 -DATABASE_URL=postgresql://kmobile_app_rw:kmobile@localhost:5432/kmobile_banking +DATABASE_URL=postgresql://kmobile_app_rw:kmobile@localhost:5431/kmobile_banking JWT_SECRET=supersecret diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3c51a6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cSpell.words": [ + "tpassword", + "tpin" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index a86210a..b956484 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "main": "index.js", "scripts": { - "dev": "nodemon src/server.js", + "dev": "npx nodemon src/server.js", "start": "node src/server.js", "lint": "eslint .", "format": "prettier --write ." diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 33ff3cd..9a90add 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,6 +1,7 @@ const authService = require('../services/auth.service'); const { generateToken } = require('../util/jwt'); const { logger } = require('../util/logger'); +const db = require('../config/db'); async function login(req, res) { const { customerNo, password } = req.body; @@ -10,18 +11,36 @@ async function login(req, res) { .status(400) .json({ error: 'customerNo and password are required' }); } - + const currentTime = new Date().toISOString(); try { const user = await authService.validateUser(customerNo, password); if (!user) return res.status(401).json({ error: 'invalid credentials' }); const token = generateToken(user.customer_no, '1d'); - res.json({ token }); + const FirstTimeLogin = await authService.CheckFirstTimeLogin(customerNo); + await db.query('UPDATE users SET last_login = $1 WHERE customer_no = $2', [ + currentTime, + customerNo, + ]); + res.json({ token, FirstTimeLogin }); } catch (err) { logger.error(err, 'login failed'); res.status(500).json({ error: 'something went wrong' }); } } +async function fetchUserDetails(req, res) { + const customerNo = req.user; + try { + const user = await authService.findUserByCustomerNo(customerNo); + if (!user) return res.status(404).json({ message: 'USER_NOT_FOUND' }); + return res.json(user); + + } catch (err) { + logger.error(err, 'error occured while fetching user details'); + res.status(500).json({ error: 'something went wrong' }); + } +} + async function tpin(req, res) { const customerNo = req.user; try { @@ -56,4 +75,31 @@ async function setTpin(req, res) { } } -module.exports = { login, tpin, setTpin }; +async function setLoginPassword(req, res) { + const customerNo = req.user; + try { + const user = await authService.findUserByCustomerNo(customerNo); + if (!user) return res.status(404).json({ error: 'USER_NOT_FOUND' }); + const { login_password } = req.body; + authService.setLoginPassword(customerNo, login_password); + return res.json({ message: 'Login Password set' }); + } catch (error) { + logger.error(error); + return res.status(500).json({ error: 'SOMETHING_WENT_WRONG' }); + } +} +async function setTransactionPassword(req, res) { + const customerNo = req.user; + try { + const user = await authService.findUserByCustomerNo(customerNo); + if (!user) return res.status(404).json({ error: 'USER_NOT_FOUND' }); + const { transaction_password } = req.body; + authService.setTransactionPassword(customerNo, transaction_password); + return res.json({ message: 'Transaction Password set' }); + } catch (error) { + logger.error(error); + return res.status(500).json({ error: 'SOMETHING_WENT_WRONG' }); + } +} + +module.exports = { login, tpin, setTpin, setLoginPassword, setTransactionPassword,fetchUserDetails }; diff --git a/src/controllers/transfer.controller.js b/src/controllers/transfer.controller.js index ea02605..2f9bc6c 100644 --- a/src/controllers/transfer.controller.js +++ b/src/controllers/transfer.controller.js @@ -5,7 +5,8 @@ async function transfer( toAccountNo, toAccountType, amount, - narration = 'tranfer from mobile' + // narration = 'transfer from mobile' + narration ) { try { const response = await axios.post( diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index 11981c5..ac8c9fb 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -5,7 +5,10 @@ const express = require('express'); const router = express.Router(); router.post('/login', authController.login); +router.get('/user_details', authenticate, authController.fetchUserDetails); router.get('/tpin', authenticate, authController.tpin); router.post('/tpin', authenticate, authController.setTpin); +router.post('/login_password', authenticate, authController.setLoginPassword); +router.post('/transaction_password', authenticate, authController.setTransactionPassword); module.exports = router; diff --git a/src/routes/transfer.route.js b/src/routes/transfer.route.js index e61b42a..7d94aea 100644 --- a/src/routes/transfer.route.js +++ b/src/routes/transfer.route.js @@ -2,10 +2,24 @@ const transferController = require('../controllers/transfer.controller'); const { logger } = require('../util/logger'); const express = require('express'); const tpinValidator = require('../validators/tpin.validator'); +const tpasswordValidator = require('../validators/tpassword.validator'); const transferValidator = require('../validators/transfer.validator'); const router = express.Router(); -router.use(tpinValidator, transferValidator); +// Added for tpassword +const passwordValidator=async(req,res,next)=>{ + const{tpin,tpassword} =req.body; + if(tpin){ + return tpinValidator(req,res,next); + } + else if(tpassword){ + return tpasswordValidator(req,res,next); + } + else{ + return res.status(400).json({error:"tpin or tpassword is required"}) + } +} +router.use(passwordValidator, transferValidator); const transferRoute = async (req, res) => { const { fromAccount, toAccount, toAccountType, amount } = req.body; diff --git a/src/services/auth.service.js b/src/services/auth.service.js index 003bdfc..db520ec 100644 --- a/src/services/auth.service.js +++ b/src/services/auth.service.js @@ -15,6 +15,14 @@ async function validateUser(customerNo, password) { return isMatch ? user : null; } +// Check First time user Login +async function CheckFirstTimeLogin(customerNo) { + const user = await findUserByCustomerNo(customerNo); + const is_first_time_login = user.is_first_login; + if (!user) return null; + return is_first_time_login; +} + async function validateTpin(customerNo, tpin) { const user = await findUserByCustomerNo(customerNo); if (!user?.tpin) return null; @@ -36,4 +44,42 @@ async function setTpin(customerNo, tpin) { } } -module.exports = { validateUser, findUserByCustomerNo, setTpin, validateTpin }; +// Set login password +async function setLoginPassword(customerNo, login_psw) { + const hashedLoginPassword = await hashPassword(login_psw); + try { + await db.query('UPDATE users SET password_hash = $1 ,is_first_login = false WHERE customer_no = $2', [ + hashedLoginPassword, + customerNo, + ]); + } catch (error) { + throw new Error( + `error occured while while setting new Login Password ${error.message}` + ); + } +} +// Validate Transaction Password +async function validateTransactionPassword(customerNo, tpassword) { + const user = await findUserByCustomerNo(customerNo); + if (!user?.transaction_password) return null; + const isMatch = await comparePassword(tpassword, user.transaction_password ); + return isMatch; +} + +// Set transaction password +async function setTransactionPassword(customerNo, trans_psw) { + const hashedTransPassword = await hashPassword(trans_psw); + try { + await db.query('UPDATE users SET transaction_password = $1 WHERE customer_no = $2', [ + hashedTransPassword, + customerNo, + ]); + } catch (error) { + throw new Error( + `error occured while while setting new Transaction Password ${error.message}` + ); + } +} + +module.exports = { validateUser, findUserByCustomerNo, setTpin, validateTpin, + CheckFirstTimeLogin, setLoginPassword, validateTransactionPassword,setTransactionPassword }; diff --git a/src/validators/tpassword.validator.js b/src/validators/tpassword.validator.js new file mode 100644 index 0000000..895b9f6 --- /dev/null +++ b/src/validators/tpassword.validator.js @@ -0,0 +1,16 @@ +const authService = require('../services/auth.service'); + +const tpasswordValidator = async (req, res, next) => { + const customerNo = req.user; + const { tpassword } = req.body; + + if (!tpassword) { + return res.status(400).json({ error: 'BAD_REQUEST' }); + } + const valid = await authService.validateTransactionPassword(customerNo, tpassword); + if (valid === null) res.status(400).json({ error: 'TransactionPassword_NOT_SET_FOR_USER' }); + if (!valid) return res.status(401).json({ error: 'INCORRECT_TransactionPassword' }); + next(); +}; + +module.exports = tpasswordValidator; diff --git a/src/validators/transfer.validator.js b/src/validators/transfer.validator.js index dfb9f61..11155cf 100644 --- a/src/validators/transfer.validator.js +++ b/src/validators/transfer.validator.js @@ -1,7 +1,7 @@ const transferValidator = async (req, res, next) => { const { fromAccount, toAccount, toAccountType, amount } = req.body; - const accountTypes = ['SB', 'LN']; + const accountTypes = ['SB', 'LN','Savings','Current']; if (!fromAccount || fromAccount.length != 11) { return res.status(400).json({ error: 'INVALID_ACCOUNT_NUMBER_FORMAT' }); }