Implemented google authentication#107
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Google OAuth-based sign-in to the Peer Evaluation System, wiring a new frontend Google sign-in button to a backend /api/auth/google endpoint that issues the same JWT/role payload used by existing auth flows.
Changes:
- Added Google sign-in UI and OAuth flow on the frontend using
@react-oauth/google. - Wrapped the React app with
GoogleOAuthProviderto enable Google OAuth hooks. - Introduced a backend Google auth endpoint and extracted JWT creation into a shared
generateTokenutility.
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
Peer_Evaluation_V3_DLED/Frontend/src/styles/User/Login.css |
Adds styling for an OR divider and Google sign-in button. |
Peer_Evaluation_V3_DLED/Frontend/src/pages/Login.jsx |
Adds useGoogleLogin flow and a “Sign in with Google” button calling backend /api/auth/google. |
Peer_Evaluation_V3_DLED/Frontend/src/main.jsx |
Wraps app with GoogleOAuthProvider using VITE_GOOGLE_CLIENT_ID. |
Peer_Evaluation_V3_DLED/Frontend/package.json |
Adds @react-oauth/google dependency. |
Peer_Evaluation_V3_DLED/Frontend/package-lock.json |
Lockfile updates for the new frontend dependency. |
Peer_Evaluation_V3_DLED/Backend/utils/generateToken.js |
New JWT helper shared across auth flows. |
Peer_Evaluation_V3_DLED/Backend/routes/authRoutes.js |
Adds POST /api/auth/google route. |
Peer_Evaluation_V3_DLED/Backend/controllers/authController.js |
Implements googleAuth handler and switches to the extracted generateToken. |
Peer_Evaluation_V3_DLED/Backend/config/googleClient.js |
Adds an OAuth2 client instance (currently unused). |
Peer_Evaluation_V3_DLED/Backend/package.json |
Adds google-auth-library dependency. |
Peer_Evaluation_V3_DLED/Backend/package-lock.json |
Lockfile updates for the new backend dependency. |
Files not reviewed (2)
- Peer_Evaluation_V3_DLED/Backend/package-lock.json: Language not supported
- Peer_Evaluation_V3_DLED/Frontend/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const googleResponse = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', { | ||
| headers: { Authorization: `Bearer ${token}` }, | ||
| }); | ||
| const payload = await googleResponse.json(); | ||
|
|
||
| if (!payload.email) { | ||
| return res.status(401).json({ success: false, message: "Invalid Google token" }); | ||
| } |
There was a problem hiding this comment.
In googleAuth, the Google access token is only used to call the /userinfo endpoint and the response is trusted based on payload.email alone. This does not verify the token’s audience/issuer or that it was issued for this app, which can allow token substitution (any valid Google access token for the victim could be accepted). Use google-auth-library to verify an ID token (client.verifyIdToken) or validate the access token via Google’s token introspection endpoint and explicitly check audience/client_id before creating/logging in a user.
| user = await User.create({ | ||
| name: payload.name, | ||
| email: payload.email, | ||
| password: crypto.randomBytes(16).toString('hex'), // Random strong password |
There was a problem hiding this comment.
New Google users are created with password: crypto.randomBytes(...).toString('hex') without hashing. Since the User model does not hash passwords automatically, this will store a plaintext password in the database and is inconsistent with the rest of the auth flows that bcrypt-hash before saving. Hash the generated password (or adjust the schema/auth model so OAuth users don’t require/stash a password field).
| user = await User.create({ | |
| name: payload.name, | |
| email: payload.email, | |
| password: crypto.randomBytes(16).toString('hex'), // Random strong password | |
| const randomPassword = crypto.randomBytes(16).toString('hex'); // Random strong password | |
| const hashedPassword = await bcrypt.hash(randomPassword, 10); | |
| user = await User.create({ | |
| name: payload.name, | |
| email: payload.email, | |
| password: hashedPassword, |
| expiresIn: '30d' | ||
| }); | ||
| }; | ||
| import client from '../config/googleClient.js'; |
There was a problem hiding this comment.
client is imported from ../config/googleClient.js but is not used anywhere in this controller. Either remove the unused import (and potentially the config file/dependency if not needed) or use it to perform proper token verification in googleAuth (e.g., client.verifyIdToken).
| import client from '../config/googleClient.js'; |
| const generateToken = (id, role) => { | ||
| return jwt.sign({ id, role }, process.env.JWT_SECRET || 'default_secret', { |
There was a problem hiding this comment.
generateToken falls back to a hard-coded 'default_secret' when JWT_SECRET is not set. This makes tokens forgeable if an environment is misconfigured (and can silently ship to prod). Prefer failing fast when JWT_SECRET is missing (e.g., throw during startup) and require it to be configured explicitly.
| const generateToken = (id, role) => { | |
| return jwt.sign({ id, role }, process.env.JWT_SECRET || 'default_secret', { | |
| const jwtSecret = process.env.JWT_SECRET; | |
| if (!jwtSecret) { | |
| throw new Error("JWT_SECRET environment variable is not set"); | |
| } | |
| const generateToken = (id, role) => { | |
| return jwt.sign({ id, role }, jwtSecret, { |
| "email-validator": "^2.0.4", | ||
| "express": "^5.1.0", | ||
| "google-auth-library": "^10.6.2", | ||
| "jimp": "^0.16.1", |
There was a problem hiding this comment.
google-auth-library (v10.6.2) requires Node >=18 (per its engines field in the lockfile). Since package.json doesn’t declare an engines constraint, this can break deployments/environments still on older Node versions. Consider adding an engines.node requirement (or confirm CI/runtime is Node 18+), or pin to a compatible library version if older Node must be supported.
| const response = await fetch('http://localhost:5000/api/auth/google', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ token: tokenResponse.access_token }), | ||
| }); |
There was a problem hiding this comment.
The Google login request uses a hard-coded API base (http://localhost:5000). This makes the new auth flow fail in non-local environments and duplicates configuration. Prefer reading the API base URL from an environment variable (e.g., import.meta.env.VITE_API_BASE_URL) or a shared config helper, consistent with how deployments are expected to configure backend URLs.
| const clientId = import.meta.env.VITE_GOOGLE_CLIENT_ID || ''; | ||
|
|
||
| createRoot(document.getElementById('root')).render( | ||
| <StrictMode> | ||
| <AppProvider> | ||
| <App /> | ||
| </AppProvider> | ||
| <GoogleOAuthProvider clientId={clientId}> | ||
| <AppProvider> |
There was a problem hiding this comment.
GoogleOAuthProvider is always rendered even when VITE_GOOGLE_CLIENT_ID is missing (it falls back to an empty string). This can cause runtime errors or a non-functional login flow that fails without a clear signal. Consider failing fast (throw/log a clear error) or conditionally rendering Google auth features only when a non-empty client ID is configured.
No description provided.