forked from CSI-KJSCE/Travel-policy-
Implemented Oauth
This commit is contained in:
@@ -1,29 +1,59 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import router from './routes/auth.js';
|
||||
import applicantRoute from './routes/applicant.js';
|
||||
import validatorRoute from './routes/validator.js';
|
||||
import generalRoute from './routes/general.js';
|
||||
import { verifyApplicantToken, verifyToken, verifyValidatorToken } from './middleware/verifyJwt.js';
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import session from "express-session";
|
||||
import passport, { initializePassport } from "./services/passportService.js";
|
||||
import router from "./routes/auth.js";
|
||||
import applicantRoute from "./routes/applicant.js";
|
||||
import validatorRoute from "./routes/validator.js";
|
||||
import generalRoute from "./routes/general.js";
|
||||
import {
|
||||
verifyApplicantToken,
|
||||
verifyToken,
|
||||
verifyValidatorToken,
|
||||
} from "./middleware/verifyJwt.js";
|
||||
|
||||
// Initialize passport strategies after environment variables are loaded
|
||||
initializePassport();
|
||||
|
||||
const app = express();
|
||||
|
||||
// Middleware setup
|
||||
app.use(cookieParser());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(cors({
|
||||
origin: true,
|
||||
credentials: true
|
||||
}));
|
||||
app.use(cookieParser());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(
|
||||
cors({
|
||||
origin: process.env.FRONTEND_URL || "http://localhost:5173",
|
||||
credentials: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// Session middleware (required for Passport)
|
||||
app.use(
|
||||
session({
|
||||
secret:
|
||||
process.env.SESSION_SECRET || "your-secret-key-change-this-in-production",
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Initialize Passport
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
// Route-specific middleware and routes
|
||||
app.use('/applicant', verifyApplicantToken, applicantRoute);
|
||||
app.use('/validator', verifyValidatorToken, validatorRoute);
|
||||
app.use('/general', verifyToken, generalRoute);
|
||||
app.use("/applicant", verifyApplicantToken, applicantRoute);
|
||||
app.use("/validator", verifyValidatorToken, validatorRoute);
|
||||
app.use("/general", verifyToken, generalRoute);
|
||||
|
||||
// Authentication routes
|
||||
app.use(router);
|
||||
|
||||
export default app;
|
||||
export default app;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import prisma from "../config/prismaConfig.js";
|
||||
import generateToken from "../services/generateToken.js";
|
||||
import passport from "passport";
|
||||
|
||||
const applicantLogin = async (req, res) => {
|
||||
try {
|
||||
@@ -8,7 +9,7 @@ const applicantLogin = async (req, res) => {
|
||||
// Check if the applicant profile exists
|
||||
const validProfile = await prisma.user.findUnique({
|
||||
where: {
|
||||
email
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -41,7 +42,13 @@ const applicantLogin = async (req, res) => {
|
||||
|
||||
// Set the token as a cookie
|
||||
return res
|
||||
.cookie("access_token", token, { sameSite: 'None', secure: true, httpOnly: true })
|
||||
.cookie("access_token", token, {
|
||||
path: "/",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
})
|
||||
.status(200)
|
||||
.json({
|
||||
message: "Login Successful",
|
||||
@@ -62,7 +69,7 @@ const validatorLogin = async (req, res) => {
|
||||
// Check if the validator profile exists
|
||||
let validProfile = await prisma.user.findUnique({
|
||||
where: {
|
||||
email
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -95,7 +102,13 @@ const validatorLogin = async (req, res) => {
|
||||
|
||||
// Set the token as a cookie
|
||||
return res
|
||||
.cookie("access_token", token, { sameSite: 'None', secure: true, httpOnly: true })
|
||||
.cookie("access_token", token, {
|
||||
path: "/",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
})
|
||||
.status(200)
|
||||
.json({
|
||||
message: "Login Successful",
|
||||
@@ -127,4 +140,67 @@ const logout = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
export { applicantLogin, validatorLogin, logout };
|
||||
//this is the controller which will handle the oauth logic
|
||||
const googleAuthStart = async (req, res, next) => {
|
||||
const designation = req.params.designation;
|
||||
|
||||
passport.authenticate("google", {
|
||||
scope: ["profile", "email"],
|
||||
state: designation,
|
||||
})(req, res, next);
|
||||
};
|
||||
|
||||
//this is the oauth callback controller
|
||||
const googleAuthCallback = async (req, res, next) => {
|
||||
try {
|
||||
const signUpIntent = req.query.state;
|
||||
const user = req.user;
|
||||
|
||||
const allowedIntents = ["validator", "applicant"];
|
||||
|
||||
if (!allowedIntents.includes(signUpIntent)) {
|
||||
return res.redirect(
|
||||
`${process.env.FRONTEND_URL || "http://localhost:5173"}/?error=invalid_intent`,
|
||||
);
|
||||
}
|
||||
|
||||
// Generate the token using correct field names from Prisma schema
|
||||
const token = generateToken({
|
||||
id: user.profileId,
|
||||
designation: user.designation,
|
||||
department: user.department,
|
||||
institute: user.institute,
|
||||
role: signUpIntent,
|
||||
});
|
||||
|
||||
// Set the token as a cookie for same-origin requests
|
||||
const cookieOptions = {
|
||||
path: "/",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
};
|
||||
|
||||
res.cookie("access_token", token, cookieOptions);
|
||||
|
||||
// For OAuth callback, also pass token in URL so frontend can set it
|
||||
// This is needed because cross-origin cookies don't work in development (different ports)
|
||||
return res.redirect(
|
||||
`${process.env.FRONTEND_URL || "http://localhost:5173"}/${signUpIntent}/dashboard?login=success&token=${token}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("OAuth callback error:", error);
|
||||
return res.redirect(
|
||||
`${process.env.FRONTEND_URL || "http://localhost:5173"}/?error=auth_failed`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
applicantLogin,
|
||||
validatorLogin,
|
||||
logout,
|
||||
googleAuthStart,
|
||||
googleAuthCallback,
|
||||
};
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
import express from 'express';
|
||||
import { applicantLogin, logout, validatorLogin } from '../controllers/authControllers.js';
|
||||
import express from "express";
|
||||
import {
|
||||
applicantLogin,
|
||||
logout,
|
||||
validatorLogin,
|
||||
googleAuthStart,
|
||||
googleAuthCallback,
|
||||
} from "../controllers/authControllers.js";
|
||||
import passport from "../services/passportService.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/applicant-login', applicantLogin);
|
||||
router.post('/validator-login', validatorLogin);
|
||||
router.post("/applicant-login", applicantLogin);
|
||||
//this route is for google oauth, this one route will handle both applicantLogic and validatorLo
|
||||
// we will be passing the designation as a URL parameter ("validator" or "applicant") and it will be passed as state through OAuth
|
||||
router.get("/auth/oauth/:designation", googleAuthStart);
|
||||
//this will be the oauth callback Route
|
||||
router.get(
|
||||
"/auth/google/callback",
|
||||
passport.authenticate("google", {
|
||||
failureRedirect: "http://localhost:5173/?error=auth_failed",
|
||||
}),
|
||||
googleAuthCallback,
|
||||
);
|
||||
|
||||
router.get('/logout', logout)
|
||||
router.post("/validator", validatorLogin);
|
||||
|
||||
export default router;
|
||||
router.get("/logout", logout);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
import app from './app.js';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
import dotenv from "dotenv";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
// Get the directory name in ES modules
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on port ${port}`);
|
||||
})
|
||||
// Load .env from backend directory
|
||||
dotenv.config({ path: path.join(__dirname, "..", ".env") });
|
||||
|
||||
// Dynamic import to ensure dotenv loads first
|
||||
const startServer = async () => {
|
||||
const { default: app } = await import("./app.js");
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on port ${port}`);
|
||||
});
|
||||
};
|
||||
|
||||
startServer().catch((error) => {
|
||||
console.error("Failed to start server:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
79
backend/src/services/passportService.js
Normal file
79
backend/src/services/passportService.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
|
||||
import prisma from "../config/prismaConfig.js";
|
||||
import passport from "passport";
|
||||
|
||||
// Function to initialize passport strategies
|
||||
export const initializePassport = () => {
|
||||
// Validate required environment variables
|
||||
if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) {
|
||||
console.error(
|
||||
"ERROR: Missing required Google OAuth credentials in environment variables.",
|
||||
);
|
||||
console.error(
|
||||
"Please ensure GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set in your .env file",
|
||||
);
|
||||
throw new Error("Missing Google OAuth credentials");
|
||||
}
|
||||
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: `${process.env.BACKEND_URL || "http://localhost:3000"}/auth/google/callback`,
|
||||
scope: ["profile", "email"],
|
||||
},
|
||||
async (accessToken, refreshToken, profile, done) => {
|
||||
//checking if theres existing user with email
|
||||
try {
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: { email: profile.emails[0]?.value },
|
||||
});
|
||||
if (existingUser) {
|
||||
return done(null, existingUser);
|
||||
}
|
||||
const newUser = await prisma.user.create({
|
||||
data: {
|
||||
userName: profile.displayName, // I am storing the name , other devs can switch to display_name based on their preferences
|
||||
email: profile.emails[0].value,
|
||||
password: "", // OAuth users don't use password authentication
|
||||
designation: "FACULTY", // Default designation, can be updated later
|
||||
auth_mode: "Google",
|
||||
OAuth_AccessToken: accessToken,
|
||||
OAuth_RefreshToken: refreshToken, //I am saving the accessTokens and refreshTokens, which MIGHT be used later
|
||||
},
|
||||
});
|
||||
console.log(
|
||||
"Passport service has made a new user: ",
|
||||
JSON.stringify(newUser),
|
||||
);
|
||||
done(null, newUser);
|
||||
} catch (err) {
|
||||
console.error("Error creating user:", err);
|
||||
done(err, null);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Serialize user for session
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user.profileId);
|
||||
});
|
||||
|
||||
// Deserialize user from session
|
||||
passport.deserializeUser(async (id, done) => {
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { profileId: id },
|
||||
});
|
||||
done(null, user);
|
||||
} catch (error) {
|
||||
done(error, null);
|
||||
}
|
||||
});
|
||||
|
||||
return passport;
|
||||
};
|
||||
|
||||
export default passport;
|
||||
Reference in New Issue
Block a user