Skip to content
Open
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
42 changes: 42 additions & 0 deletions backend/controllers/googleauth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const { OAuth2Client } = require("google-auth-library");
const User = require("../models/user");

const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

exports.googleLogin = async (req, res) => {
try {
const { token } = req.body;

const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID,
});

const payload = ticket.getPayload();
const { email, name, picture, sub } = payload;

let user = await User.findOne({ email });

if (!user) {
user = await User.create({
email,
username: email.split("@")[0],
firstName: name.split(" ")[0],
lastName: name.split(" ").slice(1).join(" "),
profileImage: picture,
password: sub,
isGoogleUser: true,
});
}

const jwtToken = user.generateToken();

res.status(200).json({
token: jwtToken,
user,
});
} catch (err) {
console.error(err);
res.status(401).json({ message: "Google login failed" });
}
};
5 changes: 3 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "Smart Rent System - A MERN Stack Application",
"main": "server.js",
"scripts": {
"scripts": {
"server": "node server.js",
"start": "node server.js",
"dev": "concurrently \"node app.js\" \"cd client && npm run dev\""
Expand All @@ -23,9 +23,10 @@
"express": "^4.21.2",
"express-mongo-sanitize": "^2.2.0",
"express-session": "^1.18.1",
"google-auth-library": "^10.5.0",
"helmet": "^8.1.0",
"joi": "^17.13.3",
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.3",
"leaflet": "^1.9.4",
"lucide-react": "^0.542.0",
"method-override": "^3.0.0",
Expand Down
3 changes: 3 additions & 0 deletions backend/routes/userRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ const router = express.Router();
const userController = require("../controllers/userController");
const { authenticate, authorize } = require("../middleware");
const { upload } = require("../cloudConfig");
const { googleLogin } = require("../controllers/googleauth");

// Public routes
router.post("/register", userController.registerUser);
router.post("/login", userController.loginUser);
router.post("/forgot-password", userController.forgotPassword);
router.post("/reset-password/:token", userController.resetPassword);
router.post("/google", googleLogin);


// Protected routes
router.post("/logout", authenticate, userController.logoutUser);
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
"proxy": "http://localhost:8000",
"dependencies": {
"@heroicons/react": "^2.2.0",
"@react-oauth/google": "^0.13.4",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^13.5.0",
"autoprefixer": "^10.4.21",
"axios": "^1.8.4",
"axios": "^1.13.2",
"leaflet": "1.9.4",
"lucide-react": "^0.544.0",
"node-fetch": "^3.3.2",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import PaymentPage from "./pages/PaymentPage";
import ResetPassword from "./pages/ResetPassword";
import ApiTest from "./pages/ApiTest";
import ProtectedRoute from "./components/ProtectedRoute";
import Dashboard from "./pages/Dashboard";

function App() {
return (
Expand All @@ -23,6 +24,7 @@ function App() {
<Route path="/listings" element={<Listings />} />
<Route path="/property/:id" element={<PropertyDetail />} />
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={<Dashboard/>} />
<Route path="/register" element={<Register />} />
<Route path="/reset-password/:token" element={<ResetPassword />} />
<Route path="/api-test" element={<ApiTest />} />
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { GoogleOAuthProvider } from "@react-oauth/google";

// Reset scroll position on page load
if (typeof window !== "undefined") {
Expand All @@ -29,10 +30,14 @@ if (typeof window !== "undefined") {
});
}

console.log("Google Client ID:", process.env.REACT_APP_GOOGLE_CLIENT_ID);

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
<GoogleOAuthProvider clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}>
<App />
</GoogleOAuthProvider>
</React.StrictMode>
);

Expand Down
12 changes: 12 additions & 0 deletions frontend/src/pages/Dashboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";

const Dashboard = () => {
return (
<div className="min-h-screen flex flex-col justify-center items-center">
<h2 className="text-2xl font-bold">User Dashboard</h2>
<p>Work in progress</p>
</div>
);
};

export default Dashboard;
71 changes: 21 additions & 50 deletions frontend/src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useLocation } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";
import { GoogleLogin } from "@react-oauth/google";
import axios from "axios";


/**
* Login Component
Expand Down Expand Up @@ -122,32 +125,29 @@ const Login = () => {
};

// Handle Google login
const handleGoogleLogin = async () => {
const handleGoogleLogin = async (res) => {
setSocialLoading("google");
setError("");

try {
// Open Google OAuth popup
const googleAuthUrl = "https://accounts.google.com/o/oauth2/v2/auth";
const redirectUri = window.location.origin + "/auth/google/callback";
const googleToken = res.credential;

const params = new URLSearchParams({
client_id:
process.env.REACT_APP_GOOGLE_CLIENT_ID || "your-google-client-id",
redirect_uri: redirectUri,
response_type: "code",
scope: "email profile",
prompt: "select_account",
});
const response = await axios.post(
"http://localhost:8000/api/users/google",
{ token: googleToken }
);

// Open the Google login popup
window.location.href = `${googleAuthUrl}?${params.toString()}`;
localStorage.setItem("token", response.data.token);
navigate("/", { replace: true });
} catch (err) {
setError("Google login failed. Please try again.");
console.error(err);
setError("Google login failed");
} finally {
setSocialLoading("");
}
};


// Handle Facebook login
const handleFacebookLogin = async () => {
setSocialLoading("facebook");
Expand Down Expand Up @@ -434,41 +434,12 @@ const Login = () => {
<div className="mt-6 grid grid-cols-2 gap-3">
{/* Google login button */}
<div>
<button
type="button"
onClick={handleGoogleLogin}
disabled={socialLoading !== ""}
className="w-full inline-flex justify-center py-2 px-4 border border-neutral-300 rounded-md shadow-sm bg-white text-sm font-medium text-neutral-700 hover:bg-neutral-50"
>
<span className="sr-only">Sign in with Google</span>
{socialLoading === "google" ? (
<span className="animate-spin h-5 w-5 border-t-2 border-r-2 border-blue-500 rounded-full" />
) : (
<svg
className="w-5 h-5"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
fill="#4285F4"
/>
<path
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
fill="#34A853"
/>
<path
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
fill="#FBBC05"
/>
<path
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
fill="#EA4335"
/>
</svg>
)}
</button>
<GoogleLogin
onSuccess={handleGoogleLogin}
onError={() => setError("Google login failed")}
useOneTap
/>

</div>

{/* Facebook login button */}
Expand Down