From ba9432de3bdfd6817951dd756247d4b0c1611af5 Mon Sep 17 00:00:00 2001 From: haseebzaki-07 Date: Sun, 10 Nov 2024 00:02:23 +0530 Subject: [PATCH] add google login/signup --- backend/.env.sample | 4 + backend/googleAuth.js | 82 ++++++++++++++++++ backend/index.js | 15 ++++ backend/package-lock.json | 144 ++++++++++++++++++++++++++++++++ backend/package.json | 3 + frontend/package-lock.json | 1 - frontend/src/Pages/Register.jsx | 17 +++- 7 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 backend/googleAuth.js diff --git a/backend/.env.sample b/backend/.env.sample index 5cd7ffa..60ef383 100644 --- a/backend/.env.sample +++ b/backend/.env.sample @@ -2,6 +2,10 @@ EMAIL_USER=your_gmail #your email EMAIL_USER=your_gmail MONGODB_URI= +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/callback +SECRET= # To create a passkey on the phone or computer you’re on: diff --git a/backend/googleAuth.js b/backend/googleAuth.js new file mode 100644 index 0000000..7dd7042 --- /dev/null +++ b/backend/googleAuth.js @@ -0,0 +1,82 @@ +import express from 'express'; +import passport from 'passport'; +import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; +import dotenv from 'dotenv'; +import User from './models/User.js'; // Assuming User model is in 'models' folder +import jwt from 'jsonwebtoken'; // Ensure you import jwt + + + +dotenv.config(); + +const router = express.Router(); + +// Passport Google OAuth Strategy +passport.use(new GoogleStrategy({ + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: process.env.GOOGLE_CALLBACK_URL, +}, async (accessToken, refreshToken, profile, done) => { + try { + // Check if the user already exists + let user = await User.findOne({ email: profile.emails[0].value }); + + if (!user) { + // If user doesn't exist, create a new user + user = new User({ + name: profile.displayName, + email: profile.emails[0].value, + password: '', // No password needed for Google login + isVerified: true, // Assume Google login means verified + otp: '', // No OTP for Google users + otpExpiry: null, // No OTP expiry for Google users + }); + await user.save(); + } + + done(null, user); + } catch (error) { + done(error, null); + } +})); + +// Serialize user into session +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +// Deserialize user from session +passport.deserializeUser(async (id, done) => { + const user = await User.findById(id); + done(null, user); +}); + +// Google OAuth routes +router.get('/auth/google', passport.authenticate('google', { + scope: ['profile', 'email'], +})); + +router.get('/auth/google/callback', passport.authenticate('google', { + failureRedirect: '/login', +}), (req, res) => { + // Generate a JWT token with necessary user information + const token = jwt.sign({ id: req.user._id, email: req.user.email }, process.env.SECRET, { expiresIn: '1h' }); + + // Set the token in a cookie + res.cookie('token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 3600000 }); // 1 hour + + // Redirect user to dashboard after successful login + res.redirect('http://localhost:5173/'); +}); + +// Logout route +router.get('/logout', (req, res) => { + req.logout((err) => { + if (err) { + return res.status(500).json({ error: 'Logout failed' }); + } + res.redirect('/'); + }); +}); + +export default router; diff --git a/backend/index.js b/backend/index.js index 48de940..a5a1f42 100644 --- a/backend/index.js +++ b/backend/index.js @@ -4,6 +4,10 @@ import connectDB from "./config/dbConnection.js"; import cookieParser from "cookie-parser"; import { createServer } from "http"; import { Server } from "socket.io"; +import passport from 'passport'; +import session from 'express-session'; +import googleAuth from "./googleAuth.js"; + @@ -24,6 +28,16 @@ app.use( app.use(cookieParser()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); +app.use(session({ + secret: 'your-secret-key', // Use a secure secret key in production + resave: false, + saveUninitialized: true, +})); + +// Initialize Passport.js +app.use(passport.initialize()); +app.use(passport.session()); + connectDB(); @@ -46,6 +60,7 @@ app.use("/api", ticketRoutes); app.use("/station", stationRoutes); app.use("/train", trainRoutes); app.use("/contact", contactUs); +app.use(googleAuth) app.get("/", (req, res) => { diff --git a/backend/package-lock.json b/backend/package-lock.json index 627068b..c18e689 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -17,11 +17,14 @@ "dotenv": "^16.4.5", "express": "^4.21.1", "express-rate-limit": "^7.4.1", + "express-session": "^1.18.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.8.0", "node": "^22.8.0", "nodemailer": "^6.9.16", "nodemon": "^3.1.7", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", "socket.io": "^4.8.0" } }, @@ -480,6 +483,14 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -1160,6 +1171,42 @@ "express": "4 || 5 || ^5.0.0-beta.1" } }, + "node_modules/express-session": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/express/node_modules/cookie": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", @@ -2407,6 +2454,11 @@ "set-blocking": "^2.0.0" } }, + "node_modules/oauth": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz", + "integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2440,6 +2492,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2497,6 +2557,61 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", + "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.10.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2527,6 +2642,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -2624,6 +2744,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3133,6 +3261,22 @@ "node": ">= 0.6" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", diff --git a/backend/package.json b/backend/package.json index 9271c14..fef1c1f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,11 +12,14 @@ "dotenv": "^16.4.5", "express": "^4.21.1", "express-rate-limit": "^7.4.1", + "express-session": "^1.18.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.8.0", "node": "^22.8.0", "nodemailer": "^6.9.16", "nodemon": "^3.1.7", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", "socket.io": "^4.8.0" }, "scripts": { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dbb281c..d391b44 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2438,7 +2438,6 @@ "version": "0.12.1", "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.1.tgz", "integrity": "sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==", - "license": "MIT", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" diff --git a/frontend/src/Pages/Register.jsx b/frontend/src/Pages/Register.jsx index d9c0fa3..a1ec6b2 100644 --- a/frontend/src/Pages/Register.jsx +++ b/frontend/src/Pages/Register.jsx @@ -7,6 +7,7 @@ import { MdAttachEmail, MdOutlinePassword } from "react-icons/md"; import { registerValidation } from "../validations/validation"; import Backdrop from "@mui/material/Backdrop"; import CircularProgress from "@mui/material/CircularProgress"; +import { GoogleLogin } from '@react-oauth/google'; const Register = () => { useEffect(() => { @@ -86,7 +87,17 @@ const Register = () => { SetLoader(false); } }; - + const handleGoogleLogin = async (response) => { + SetLoader(true); + try { + // Redirect user to Google OAuth authentication + window.location.href = "http://localhost:3000/auth/google"; // This will trigger Google login process + } catch (error) { + setConfirmationMessage("Google login failed. Please try again."); + } + SetLoader(false); + }; + useEffect(() => { if (confirmationMessage) { const timer = setTimeout(() => setConfirmationMessage(""), 3000); @@ -276,9 +287,7 @@ const Register = () => {