Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@
"crypto": "^1.0.1",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-session": "^1.18.1",
"google-auth-library": "^9.15.1",
"joi": "^17.13.3",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"nodemailer": "^6.10.0",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
Expand Down
26 changes: 12 additions & 14 deletions src/controllers/auth.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { googleVerifyIdToken } from '../utils/googleVerifyToken.utils.js';

/* eslint no-undef:off */
// Authentication Controllers
export const signup = async (req, res) => {
export const signup = async (req, res, next) => {
try {
const { error } = signupValidation(req.body);
if (error) {
Expand Down Expand Up @@ -79,11 +79,11 @@ export const signup = async (req, res) => {
user: user,
});
} catch (error) {
return res.status(500).json({ message: `Signup failed: ${error.message}` });
next(error);
}
};

export const verifyEmail = async (req, res) => {
export const verifyEmail = async (req, res, next) => {
try {
const { error } = verifyEmailValidation(req.body);
if (error) {
Expand Down Expand Up @@ -120,11 +120,11 @@ export const verifyEmail = async (req, res) => {

return res.status(200).json({ message: 'Email verified successfully' });
} catch (error) {
return res.status(500).json({ message: 'Verification failed', error });
next(error);
}
};

export const signin = async (req, res) => {
export const signin = async (req, res, next) => {
try {
const { email, password } = req.body;

Expand Down Expand Up @@ -178,11 +178,11 @@ export const signin = async (req, res) => {
},
});
} catch (error) {
return res.status(500).json({ message: 'Signin failed', error });
next(error);
}
};

export const forgotPassword = async (req, res) => {
export const forgotPassword = async (req, res, next) => {
try {
const { error } = forgotPasswordValidation();
if (error) {
Expand Down Expand Up @@ -225,13 +225,11 @@ export const forgotPassword = async (req, res) => {
message: 'Password reset OTP sent',
});
} catch (error) {
return res
.status(500)
.json({ message: 'Password reset request failed', error });
next(error);
}
};

export const resetPassword = async (req, res) => {
export const resetPassword = async (req, res, next) => {
try {
const { error } = resetPasswordValidation();
if (error) {
Expand Down Expand Up @@ -274,11 +272,11 @@ export const resetPassword = async (req, res) => {

return res.status(200).json({ message: 'Password reset successfully' });
} catch (error) {
return res.status(500).json({ message: 'Password reset failed', error });
next(error);
}
};

export const refreshAccessToken = async (req, res) => {
export const refreshAccessToken = async (req, res, next) => {
try {
const { refreshToken } = req.body;

Expand Down Expand Up @@ -308,7 +306,7 @@ export const refreshAccessToken = async (req, res) => {
accessToken: newAccessToken,
});
} catch (error) {
return res.status(401).json({ message: 'Token refresh failed', error });
next(error);
}
};

Expand Down
22 changes: 22 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import express from 'express';
import * as dotenv from 'dotenv';
dotenv.config();
import morgan from 'morgan';
import helmet from 'helmet';
import cors from 'cors';
import { apiLimiter } from './utils/apiLimiter.js';
import passport from 'passport';
import session from 'express-session';
import authRouter from './routes/auth.routes.js';
import { errorHandler, notFound } from './middlewares/errorHandler.js';
import { configureGoogleStrategy } from './strategies/google-strategy.js';

/* eslint no-undef: off */
const PORT = process.env.PORT;

const app = express();

app.use(
Expand All @@ -25,13 +31,29 @@ app.use(
app.use(passport.initialize());
app.use(passport.session());

//// Cors Policy
app.use(cors());

// Helmet helps you secure your Express apps by setting various HTTP headers.
app.use(helmet());
app.use(helmet.contentSecurityPolicy());

configureGoogleStrategy();

// Rate limiter middleware
app.use(apiLimiter);

//morgan is a HTTP request logger middleware for Node.js
app.use(morgan('dev'));
app.use(express.json()); // for parsing application/json
app.use(express.urlencoded({ extended: true }));

app.use(authRouter);

// Error handling middleware
app.use(notFound);
app.use(errorHandler);

app.listen(PORT, () => {
/* eslint no-console:off */
console.log(
Expand Down
14 changes: 14 additions & 0 deletions src/middlewares/errorHandler.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const notFound = (req, res, next) => {
const error = new Error(`Resource not found - ${req.originalUrl}`);
res.status(404);
next(error);
};
/* eslint-disable */
export const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode).json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? null : err.stack, // Hide stack trace in production
statusCode,
});
};
13 changes: 7 additions & 6 deletions src/routes/auth.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import {
googleOAuthCallback,
googleOAuthLogin,
} from '../controllers/auth.controller.js';
import { apiLimiter } from '../utils/apiLimiter.js';

const router = Router();

router.post('/api/auth/signup', signup);
router.post('/api/auth/verifyEmail', verifyEmail);
router.post('/api/auth/signin', signin);
router.post('/api/auth/forgotPassword', forgotPassword);
router.post('/api/auth/resetPassword', resetPassword);
router.post('/api/auth/refreshAccessToken', refreshAccessToken);
router.post('/api/auth/signup', apiLimiter, signup);
router.post('/api/auth/verifyEmail', apiLimiter, verifyEmail);
router.post('/api/auth/signin', apiLimiter, signin);
router.post('/api/auth/forgotPassword', apiLimiter, forgotPassword);
router.post('/api/auth/resetPassword', apiLimiter, resetPassword);
router.post('/api/auth/refreshAccessToken', apiLimiter, refreshAccessToken);

// Google OAuth Routes
// Initiate Google OAuth authentication
Expand Down
12 changes: 12 additions & 0 deletions src/utils/apiLimiter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import rateLimit from 'express-rate-limit';

export const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
message: {
status: 429,
message: 'Too many requests, please try again later.',
},
});