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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ backend/__pycache__/
*.py[cod]
*.pyo
backend/coverage_html/

# Compiled binaries
*.so
*.dll
Expand Down
4 changes: 4 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from fastapi.staticfiles import StaticFiles
from api.v1.endpoints import search
from api.v1.endpoints.chatbot import huggingface
from routes import google_auth
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()

Expand All @@ -26,12 +28,14 @@
allow_methods=["*"], # Allow all methods (GET, POST, etc.)
allow_headers=["*"], # Allow all headers
)
app.add_middleware(SessionMiddleware, secret_key="your_super_secret_key")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Hardcoded session secret. Danger detected.

Use env + secure cookie flags. This backs Authlib state protection.

+import os
@@
-app.add_middleware(SessionMiddleware, secret_key="your_super_secret_key")
+app.add_middleware(
+    SessionMiddleware,
+    secret_key=os.getenv("SESSION_SECRET_KEY", "change-me-in-prod"),
+    same_site="lax",
+    https_only=True,
+)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.add_middleware(SessionMiddleware, secret_key="your_super_secret_key")
import os
app.add_middleware(
SessionMiddleware,
secret_key=os.getenv("SESSION_SECRET_KEY", "change-me-in-prod"),
same_site="lax",
https_only=True,
)
🤖 Prompt for AI Agents
In backend/main.py around line 31, the app currently uses a hardcoded session
secret; replace that with a secret read from the environment (e.g.
SESSION_SECRET) and fail fast if it's missing, and when adding SessionMiddleware
set secure cookie flags (secure=True, httponly=True, samesite='lax' or 'strict')
so the session cookie is only sent over HTTPS and not accessible from JS; keep
the secret coming from os.environ (no hardcoded fallback in production) and
ensure the middleware call uses the env secret and the secure cookie options.


# Create tables
Base.metadata.create_all(bind=engine)

# Include routers
app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
app.include_router(google_auth.router, prefix="/auth/google", tags=["Google Authentication"])
app.include_router(profile.router, prefix="/profile", tags=["User Profile"])
app.include_router(post.router,prefix="/posts", tags=["Posts"])
app.include_router(postReaction.router, prefix="/interactions", tags=["Post Interactions"])
Expand Down
2 changes: 1 addition & 1 deletion backend/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class User(Base):
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True, nullable=False)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
hashed_password = Column(String, nullable=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Reconsider making hashed_password nullable. Integrity compromised.

Current flow always sets a random password for Google users. Keep NOT NULL and migrate existing rows, or justify nulls and harden all auth paths accordingly.

-    hashed_password = Column(String, nullable=True)
+    hashed_password = Column(String, nullable=False)

🏁 Script executed:

#!/bin/bash
# Scan for places assuming non-null hashed_password or comparing to None.
rg -nP --type=py -C2 '\bhashed_password\b'

Length of output: 7200


Make hashed_password NOT NULL and backfill existing rows

Auth paths assume a password exists (verify_password uses user.hashed_password; Google flow writes a hashed password). Backfill any NULL hashed_password rows (generate secure random passwords and hash them, or set an unusable hash + force reset) before altering the column to NOT NULL.

-    hashed_password = Column(String, nullable=True)
+    hashed_password = Column(String, nullable=False)

Locations: backend/models/user.py; backend/services/AuthHandler.py (verify_password usage); backend/routes/google_auth.py (hashed_password assignment); backend/tests/* (fixtures may need updates).

Committable suggestion skipped: line range outside the PR's diff.

is_active = Column(Boolean, default=True)
is_verified = Column(Boolean, default=False) # Tracks email verification status
otp = Column(String, nullable=True) # Stores the OTP temporarily
Expand Down
68 changes: 68 additions & 0 deletions backend/routes/google_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from fastapi import APIRouter, Request, Depends, HTTPException
from fastapi.responses import RedirectResponse
from authlib.integrations.starlette_client import OAuth
from starlette.config import Config
from starlette.requests import Request
from sqlalchemy.orm import Session
from core.dependencies import get_db
from models.user import User
from services.AuthHandler import AuthHandler
from core.security import create_access_token
import os

router = APIRouter()
Comment on lines +1 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick

Tidy imports. Remove duplicates/unused.

  • Duplicate Request import.
  • Unused AuthHandler.
  • SECRET_KEY in config_data unused.
-from fastapi import APIRouter, Request, Depends, HTTPException
-from fastapi.responses import RedirectResponse
-from authlib.integrations.starlette_client import OAuth
-from starlette.config import Config
-from starlette.requests import Request
-from sqlalchemy.orm import Session
-from core.dependencies import get_db
-from models.user import User
-from services.AuthHandler import AuthHandler
-from core.security import create_access_token
-import os
+from fastapi import APIRouter, Request, Depends, HTTPException
+from fastapi.responses import RedirectResponse
+from authlib.integrations.starlette_client import OAuth
+from starlette.config import Config
+from sqlalchemy.orm import Session
+from sqlalchemy import exists
+from core.dependencies import get_db
+from models.user import User
+from core.security import create_access_token
+import os
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from fastapi import APIRouter, Request, Depends, HTTPException
from fastapi.responses import RedirectResponse
from authlib.integrations.starlette_client import OAuth
from starlette.config import Config
from starlette.requests import Request
from sqlalchemy.orm import Session
from core.dependencies import get_db
from models.user import User
from services.AuthHandler import AuthHandler
from core.security import create_access_token
import os
router = APIRouter()
from fastapi import APIRouter, Request, Depends, HTTPException
from fastapi.responses import RedirectResponse
from authlib.integrations.starlette_client import OAuth
from starlette.config import Config
from sqlalchemy.orm import Session
from sqlalchemy import exists
from core.dependencies import get_db
from models.user import User
from core.security import create_access_token
import os
router = APIRouter()
🤖 Prompt for AI Agents
In backend/routes/google_auth.py around lines 1 to 13, tidy up imports by
removing the duplicate Request import (keep only one from fastapi or starlette
as needed), remove the unused AuthHandler import, and delete or stop populating
the unused SECRET_KEY entry in any config_data or Config usage; ensure remaining
imports are minimal and only include Request, APIRouter, Depends, HTTPException,
RedirectResponse, OAuth, Config (if used), Session, get_db, User,
create_access_token, and os as required.


config_data = {
"GOOGLE_CLIENT_ID": os.getenv("GOOGLE_CLIENT_ID"),
"GOOGLE_CLIENT_SECRET": os.getenv("GOOGLE_CLIENT_SECRET"),
"SECRET_KEY": os.getenv("SECRET_KEY", "secret"),
}
config = Config(environ=config_data)
oauth = OAuth(config)
Comment on lines +15 to +21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate env at startup. Fail fast if missing.

Ensure client id/secret present to avoid runtime 500s.

 config_data = {
     "GOOGLE_CLIENT_ID": os.getenv("GOOGLE_CLIENT_ID"),
     "GOOGLE_CLIENT_SECRET": os.getenv("GOOGLE_CLIENT_SECRET"),
-    "SECRET_KEY": os.getenv("SECRET_KEY", "secret"),
 }
 config = Config(environ=config_data)
+if not config_data["GOOGLE_CLIENT_ID"] or not config_data["GOOGLE_CLIENT_SECRET"]:
+    raise RuntimeError("GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET must be set")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
config_data = {
"GOOGLE_CLIENT_ID": os.getenv("GOOGLE_CLIENT_ID"),
"GOOGLE_CLIENT_SECRET": os.getenv("GOOGLE_CLIENT_SECRET"),
"SECRET_KEY": os.getenv("SECRET_KEY", "secret"),
}
config = Config(environ=config_data)
oauth = OAuth(config)
config_data = {
"GOOGLE_CLIENT_ID": os.getenv("GOOGLE_CLIENT_ID"),
"GOOGLE_CLIENT_SECRET": os.getenv("GOOGLE_CLIENT_SECRET"),
"SECRET_KEY": os.getenv("SECRET_KEY"),
}
if not config_data["GOOGLE_CLIENT_ID"] or not config_data["GOOGLE_CLIENT_SECRET"]:
raise RuntimeError("GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET must be set")
config = Config(environ=config_data)
oauth = OAuth(config)
🤖 Prompt for AI Agents
In backend/routes/google_auth.py around lines 15 to 21, the code constructs an
OAuth Config from environment variables but does not validate that
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set; add a startup validation that
reads those two vars, and if either is missing log a clear error and fail fast
(raise a RuntimeError or call sys.exit(1)) with a descriptive message so the
application does not start and later produce 500s; leave SECRET_KEY default
behavior intact or validate it similarly if desired.

oauth.register(
name='google',
client_id=config_data["GOOGLE_CLIENT_ID"],
client_secret=config_data["GOOGLE_CLIENT_SECRET"],
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email profile'},
)

@router.get("/login")
async def google_login(request: Request):
redirect_uri = os.getenv("GOOGLE_REDIRECT_URI")
return await oauth.google.authorize_redirect(request, redirect_uri)

Comment on lines +30 to +34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Compute redirect URI via url_for. Robustness++

Avoid hardcoding GOOGLE_REDIRECT_URI; derive from routing.

 @router.get("/login")
 async def google_login(request: Request):
-    redirect_uri = os.getenv("GOOGLE_REDIRECT_URI")
+    # Build absolute callback URL under current host
+    redirect_uri = str(request.url_for("google_callback"))
     return await oauth.google.authorize_redirect(request, redirect_uri)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@router.get("/login")
async def google_login(request: Request):
redirect_uri = os.getenv("GOOGLE_REDIRECT_URI")
return await oauth.google.authorize_redirect(request, redirect_uri)
@router.get("/login")
async def google_login(request: Request):
# Build absolute callback URL under current host
redirect_uri = str(request.url_for("google_callback"))
return await oauth.google.authorize_redirect(request, redirect_uri)
🤖 Prompt for AI Agents
In backend/routes/google_auth.py around lines 30 to 34, the redirect URI is
currently pulled from the environment; instead compute it from the router/app
routes to avoid hardcoding. Use the request.url_for (or
request.app.url_path_for) with the callback endpoint name (e.g.,
"google_callback") to build the full redirect URI and pass that to
oauth.google.authorize_redirect; keep a fallback to the env var if url_for
fails, and ensure the callback route is named or registered so url_for resolves
correctly.

@router.get("/callback")
async def google_callback(request: Request, db: Session = Depends(get_db)):
token = await oauth.google.authorize_access_token(request)
print("Google token response:", token)
user_info = token.get("userinfo")
if not user_info:
raise HTTPException(status_code=400, detail="No userinfo in Google response") # Find or create user
Comment on lines +35 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Do not print tokens. Retrieve user info correctly.

  • Remove sensitive print.
  • Prefer parsing ID token or calling userinfo endpoint.
 async def google_callback(request: Request, db: Session = Depends(get_db)):
-    token = await oauth.google.authorize_access_token(request)
-    print("Google token response:", token)
-    user_info = token.get("userinfo")
+    token = await oauth.google.authorize_access_token(request)
+    # Parse verified claims from ID token
+    user_info = await oauth.google.parse_id_token(request, token)
     if not user_info:
         raise HTTPException(status_code=400, detail="No userinfo in Google response")    # Find or create user
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@router.get("/callback")
async def google_callback(request: Request, db: Session = Depends(get_db)):
token = await oauth.google.authorize_access_token(request)
print("Google token response:", token)
user_info = token.get("userinfo")
if not user_info:
raise HTTPException(status_code=400, detail="No userinfo in Google response") # Find or create user
@router.get("/callback")
async def google_callback(request: Request, db: Session = Depends(get_db)):
token = await oauth.google.authorize_access_token(request)
# Parse verified claims from ID token
user_info = await oauth.google.parse_id_token(request, token)
if not user_info:
raise HTTPException(status_code=400, detail="No userinfo in Google response") # Find or create user
🤖 Prompt for AI Agents
In backend/routes/google_auth.py around lines 35 to 41, remove the sensitive
print of the full token response and instead retrieve verified user information
by parsing the ID token (JWT) or by calling Google's userinfo endpoint via the
oauth client; specifically, drop the print statement, extract and validate the
id_token claims (or call oauth.google.get("userinfo", token=token) /
oauth.google.userinfo endpoint) to obtain required fields (email, sub, name),
handle missing/invalid claims by raising HTTPException(400), and proceed with
find-or-create logic using the validated user data.

user = db.query(User).filter(User.email == user_info["email"]).first()
if not user:
from core.security import hash_password
# Generate a strong random password for Google OAuth users
# They won't use this password since they'll always login via Google
import secrets
random_password = secrets.token_urlsafe(16)
hashed_password = hash_password(random_password)

# Create username from email and make it unique with a random suffix
base_username = user_info["email"].split("@")[0]
username = f"{base_username}{secrets.token_hex(3)}"

user = User(
username=username,
email=user_info["email"],
hashed_password=hashed_password,
is_verified=True,
profile_completed=False,
)
db.add(user)
db.commit()
db.refresh(user)# Generate JWT token
Comment on lines +42 to +64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Username collision + email_verified handling + DB error path.

Guarantee unique username deterministically; honor Google’s email_verified; rollback on failures.

-    user = db.query(User).filter(User.email == user_info["email"]).first()
+    email = user_info.get("email")
+    if not email:
+        raise HTTPException(status_code=400, detail="Google profile missing email")
+    user = db.query(User).filter(User.email == email).first()
     if not user:
-        from core.security import hash_password
+        from core.security import hash_password
         # Generate a strong random password for Google OAuth users
         # They won't use this password since they'll always login via Google
         import secrets
         random_password = secrets.token_urlsafe(16)
         hashed_password = hash_password(random_password)
         
         # Create username from email and make it unique with a random suffix
-        base_username = user_info["email"].split("@")[0]
-        username = f"{base_username}{secrets.token_hex(3)}"
+        base_username = email.split("@")[0]
+        candidate = base_username
+        suffix = 0
+        while db.query(exists().where(User.username == candidate)).scalar():
+            suffix += 1
+            candidate = f"{base_username}{suffix}"
+        username = candidate
         
         user = User(
             username=username,
-            email=user_info["email"],
+            email=email,
             hashed_password=hashed_password,
-            is_verified=True,
+            is_verified=bool(user_info.get("email_verified", True)),
             profile_completed=False,
         )
-        db.add(user)
-        db.commit()
-        db.refresh(user)# Generate JWT token
+        try:
+            db.add(user)
+            db.commit()
+            db.refresh(user)
+        except Exception:
+            db.rollback()
+            raise
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user = db.query(User).filter(User.email == user_info["email"]).first()
if not user:
from core.security import hash_password
# Generate a strong random password for Google OAuth users
# They won't use this password since they'll always login via Google
import secrets
random_password = secrets.token_urlsafe(16)
hashed_password = hash_password(random_password)
# Create username from email and make it unique with a random suffix
base_username = user_info["email"].split("@")[0]
username = f"{base_username}{secrets.token_hex(3)}"
user = User(
username=username,
email=user_info["email"],
hashed_password=hashed_password,
is_verified=True,
profile_completed=False,
)
db.add(user)
db.commit()
db.refresh(user)# Generate JWT token
email = user_info.get("email")
if not email:
raise HTTPException(status_code=400, detail="Google profile missing email")
user = db.query(User).filter(User.email == email).first()
if not user:
from core.security import hash_password
# Generate a strong random password for Google OAuth users
# They won't use this password since they'll always login via Google
import secrets
random_password = secrets.token_urlsafe(16)
hashed_password = hash_password(random_password)
# Create username from email and make it unique with a random suffix
base_username = email.split("@")[0]
candidate = base_username
suffix = 0
while db.query(exists().where(User.username == candidate)).scalar():
suffix += 1
candidate = f"{base_username}{suffix}"
username = candidate
user = User(
username=username,
email=email,
hashed_password=hashed_password,
is_verified=bool(user_info.get("email_verified", True)),
profile_completed=False,
)
try:
db.add(user)
db.commit()
db.refresh(user)
except Exception:
db.rollback()
raise
🤖 Prompt for AI Agents
In backend/routes/google_auth.py around lines 42 to 64, the current code naively
generates a username with a random suffix (risking collisions), ignores Google’s
email_verified flag, and doesn’t handle DB failures atomically; change to derive
a base username from the email and then deterministically ensure uniqueness by
checking the DB in a short loop (append incremental numeric suffixes until an
unused username is found), set user.is_verified according to
user_info.get("email_verified", False) instead of always True, and wrap the
creation in a try/except with db.rollback() on exception and proper re-raise or
error handling so partial writes are not persisted; also ensure commit() and
refresh() are only called after the user is added successfully.

access_token = create_access_token({"sub": user.username})
# Redirect to frontend with token as query param
frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5173")
return RedirectResponse(f"{frontend_url}/login?token={access_token}")
Comment on lines +65 to +68

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid sending JWT in query string. Use fragment or HttpOnly cookie.

Query params leak via Referer. Prefer:

  • Fragment: /login#token=...
  • Or set HttpOnly cookie and redirect without token.

Pick one and align frontend.

-    access_token = create_access_token({"sub": user.username})
-    # Redirect to frontend with token as query param
-    frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5173")
-    return RedirectResponse(f"{frontend_url}/login?token={access_token}")
+    access_token = create_access_token({"sub": user.username})
+    frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5173")
+    # Option A: fragment to avoid Referer leakage
+    return RedirectResponse(f"{frontend_url}/login#token={access_token}")
+    # Option B (preferred in prod): set HttpOnly cookie and redirect without token
+    # resp = RedirectResponse(f"{frontend_url}/dashboard/posts")
+    # resp.set_cookie("access_token", access_token, httponly=True, samesite="lax", secure=True, max_age=12*3600)
+    # return resp
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
access_token = create_access_token({"sub": user.username})
# Redirect to frontend with token as query param
frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5173")
return RedirectResponse(f"{frontend_url}/login?token={access_token}")
access_token = create_access_token({"sub": user.username})
frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5173")
# Option A: fragment to avoid Referer leakage
return RedirectResponse(f"{frontend_url}/login#token={access_token}")
# Option B (preferred in prod): set HttpOnly cookie and redirect without token
# resp = RedirectResponse(f"{frontend_url}/dashboard/posts")
# resp.set_cookie("access_token", access_token, httponly=True, samesite="lax", secure=True, max_age=12*3600)
# return resp
🤖 Prompt for AI Agents
In backend/routes/google_auth.py around lines 65 to 68, the code returns the JWT
as a query parameter which can leak via Referer; instead set the JWT as a
secure, HttpOnly cookie on the response and redirect without including the token
in the URL: create the access token, build a RedirectResponse to the frontend
URL without token, call response.set_cookie(name="access_token",
value=access_token, httponly=True, secure=True, samesite="Lax" (or "Strict" if
appropriate), path="/") and return that response so the frontend reads auth from
the cookie (update frontend to use cookie-based auth) .

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"test:coverage": "vitest run --coverage"
},
"dependencies": {
"@react-oauth/google": "^0.12.2",
"@tailwindcss/vite": "^4.0.9",
"axios": "^1.8.1",
"date-fns": "^4.1.0",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import VerifyEmail from "./pages/VerifyEmail";
import ResetPassword from "./pages/ResetPassword";
import ForgotPassword from "./pages/ForgotPassword";


const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;

Comment on lines +17 to +19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick

Remove unused GOOGLE_CLIENT_ID. Minimalism engaged.

Dead code. Delete to avoid confusion.

-const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
🤖 Prompt for AI Agents
In frontend/src/App.jsx around lines 17 to 19, the const GOOGLE_CLIENT_ID =
import.meta.env.VITE_GOOGLE_CLIENT_ID; is dead code; remove this declaration
(and any direct references to GOOGLE_CLIENT_ID in this file), and if no other
files reference VITE_GOOGLE_CLIENT_ID, delete any code that only exists to
supply it; run lint/ESBuild to ensure no unused-variable or import.meta.env
references remain and commit the simplified file.

function App() {
return (
<Router>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App"; // Ensure this path is correct
import './index.css';
import { GoogleOAuthProvider } from '@react-oauth/google';

const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick

Guard missing client ID to fail fast.

Emit a warning if VITE_GOOGLE_CLIENT_ID is absent.

-const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
+const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
+if (!GOOGLE_CLIENT_ID) {
+  console.warn("VITE_GOOGLE_CLIENT_ID is missing. Google login will not work.");
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
if (!GOOGLE_CLIENT_ID) {
console.warn("VITE_GOOGLE_CLIENT_ID is missing. Google login will not work.");
}
🤖 Prompt for AI Agents
In frontend/src/index.jsx around line 7, the GOOGLE_CLIENT_ID is read directly
from import.meta.env without a guard; add a runtime check right after this line
that verifies VITE_GOOGLE_CLIENT_ID is present and non-empty, and if not call
console.warn with a clear message about the missing env var and stop further
Google auth initialization (fail fast by returning early or throwing an Error
depending on surrounding flow) so the app emits a warning and avoids continuing
with an undefined client ID.


ReactDOM.createRoot(document.getElementById("root")).render(
<App />
<GoogleOAuthProvider clientId={GOOGLE_CLIENT_ID}>
<App />
</GoogleOAuthProvider>
);
Comment on lines +5 to 13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Decide the flow: backend-redirect OR One‑Tap. Do not mix.

If you keep server‑redirect, you do not need GoogleOAuthProvider at all. If you keep One‑Tap, do not redirect in Login.jsx. Pick one.

Do you plan to use server‑redirect (Authlib) exclusively? If yes, I will generate a cleanup patch to remove the provider and dependency.


Affirmative. Initiating code inspection.


🏁 Script executed:

#!/bin/bash
# Locate Login.jsx in frontend source
LOGIN_FILE=$(find frontend/src -type f -name "Login.jsx" | head -n1)
echo "Login file: $LOGIN_FILE"

# Inspect for client-side redirect usage
if [[ -n "$LOGIN_FILE" ]]; then
  rg -n "redirect" "$LOGIN_FILE" || echo "No 'redirect' usage found"
  rg -n "location" "$LOGIN_FILE" || echo "No 'location' usage found"
else
  echo "Login.jsx not found"
fi

# Check for One-Tap references and provider imports
rg -n "OneTap" -tjs frontend/src || echo "No OneTap references found"
rg -n "@react-oauth/google" -tjs frontend/src || echo "No provider import references found"

Length of output: 639


Choose one auth flow: backend-redirect OR One‑Tap — do not mix.

Detected mixing in repo:

  • frontend/src/pages/Login.jsx — server redirect: window.location.href = ${import.meta.env.VITE_API_URL}/auth/google/login (line 21); client redirect to "/dashboard/posts" (line 43).
  • frontend/src/pages/Login.jsx — imports GoogleLogin, googleLogout (line 7) and uses useOneTap (line 132).
  • frontend/src/index.jsx — imports GoogleOAuthProvider (line 5).

Action required:

  • Pick one flow.
    • Backend-redirect: remove GoogleOAuthProvider and all @react-oauth/google usages (index.jsx and pages/Login.jsx imports/usages).
    • One‑Tap: remove the server redirect in Login.jsx and use client-side One‑Tap token flow exclusively.

I will generate a cleanup patch to remove the provider and client One‑Tap code if you confirm server-redirect only.

🤖 Prompt for AI Agents
In frontend/src/index.jsx around lines 5-13 there is a GoogleOAuthProvider
wrapper while the app also uses a server-redirect flow in pages/Login.jsx — you
must not mix One‑Tap/client SDK and backend-redirect. Pick backend-redirect:
remove the GoogleOAuthProvider and any @react-oauth/google import/usages from
frontend/src/index.jsx (replace the wrapped render with a plain App render) and
then in frontend/src/pages/Login.jsx remove GoogleLogin, googleLogout imports
and any useOneTap/One‑Tap-related code paths so only the server redirect
(window.location.href = `${import.meta.env.VITE_API_URL}/auth/google/login`)
remains; ensure no leftover references to the provider/SDK and run the app to
verify login still redirects to the backend endpoint.

35 changes: 30 additions & 5 deletions frontend/src/pages/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { login } from "../api/auth";
import axios from "axios";
import { useAuth } from "../context/AuthContext";
import Navbar from "../components/Navbar";
import { GoogleLogin, googleLogout } from '@react-oauth/google';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick

Remove unused import.

googleLogout is not used.

-import { GoogleLogin, googleLogout } from '@react-oauth/google';
+import { GoogleLogin } from '@react-oauth/google';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { GoogleLogin, googleLogout } from '@react-oauth/google';
import { GoogleLogin } from '@react-oauth/google';
🤖 Prompt for AI Agents
In frontend/src/pages/Login.jsx around line 7, the import statement includes
googleLogout which is not used; remove googleLogout from the import (keep
GoogleLogin) to eliminate the unused import and update any related code/comments
if needed.


const Login = () => {
const [formData, setFormData] = useState({ username: "", password: "" });
Expand All @@ -14,12 +15,35 @@ const Login = () => {
const { login: authLogin } = useAuth();
const location = useLocation();

const handleGoogleSuccess = async (credentialResponse) => {
try {
// Option 1: Use backend OAuth flow (recommended)
window.location.href = `${import.meta.env.VITE_API_URL}/auth/google/login`;

// Option 2: If using Google One Tap and want to POST token to backend:
// const { credential } = credentialResponse;
// const res = await axios.post(`${import.meta.env.VITE_API_URL}/auth/google/onetap`, { token: credential });
// Handle login as usual with res.data.access_token
} catch (error) {
setError("Google login failed");
}
};
Comment on lines +18 to +30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Flow mismatch: GoogleLogin onSuccess triggers server redirect. Illogical.

Pick one:

  • Server‑redirect (Authlib): no GoogleLogin; just a button/link to /auth/google/login.
  • One‑Tap: keep GoogleLogin and POST credential to backend /onetap.

Current mix will misfire. Recommend server‑redirect cleanup below.

-  const handleGoogleSuccess = async (credentialResponse) => {
-    try {
-      // Option 1: Use backend OAuth flow (recommended)
-      window.location.href = `${import.meta.env.VITE_API_URL}/auth/google/login`;
-
-      // Option 2: If using Google One Tap and want to POST token to backend:
-      // const { credential } = credentialResponse;
-      // const res = await axios.post(`${import.meta.env.VITE_API_URL}/auth/google/onetap`, { token: credential });
-      // Handle login as usual with res.data.access_token
-    } catch (error) {
-      setError("Google login failed");
-    }
-  };
+  // If using server-redirect, no handler needed here.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/src/pages/Login.jsx around lines 18 to 30, the Google login handler
mixes two incompatible flows: it triggers a server redirect while being used as
a GoogleLogin onSuccess handler. Remove the redirect from the onSuccess handler
and choose one flow: either replace the GoogleLogin control with a plain
link/button that navigates to
`${import.meta.env.VITE_API_URL}/auth/google/login` (server OAuth redirect
flow), or keep the GoogleLogin component and change the handler to POST the
credential to your backend endpoint (e.g. `/auth/google/onetap`) and handle the
returned token; implement the chosen flow consistently and remove the
dead/commented alternative to avoid confusion.

useEffect(() => {
if (location.state?.fromVerification) {
setSuccess("Email verified! Please log in to continue.");
}
}, [location.state]);

useEffect(() => {
const params = new URLSearchParams(window.location.search);
const token = params.get("token");
if (token) {
localStorage.setItem("token", token);
// Optionally fetch user and redirect
window.location.href = "/dashboard/posts";
}
}, []);
Comment on lines +37 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick

Token via query string works, but leaks via Referer.

Safer options:

  • Use URL fragment (#token=...) and parse it.
  • Or set HttpOnly cookie from backend and redirect without token.

Front/back must align.

Do you prefer fragment or cookie? I can patch both sides accordingly.

🤖 Prompt for AI Agents
In frontend/src/pages/Login.jsx around lines 37 to 45 the current implementation
reads the auth token from the query string and stores it in localStorage then
redirects, which leaks the token via Referer; change to one of two safer flows:
(A) Client-side fragment flow — read token from window.location.hash (e.g. parse
#token=...), store it in localStorage/sessionStorage, then remove the fragment
from the URL (history.replaceState) before redirecting so the token never
appears in Referer; or (B) Server-set HttpOnly cookie flow — stop passing token
in the URL, have the backend set an HttpOnly, Secure cookie on successful auth
and simply redirect to /dashboard/posts (client reads no token), update backend
and front to rely on cookie-based auth; choose fragment if you want minimal
backend work or cookie if you prefer maximal security and update both sides
accordingly.


const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
Expand Down Expand Up @@ -101,13 +125,14 @@ const Login = () => {
type="button"
className="w-full flex items-center justify-center gap-2 border border-gray-300 rounded-full py-2 mb-3 hover:bg-gray-100"
>
<img
src="/education-svgrepo-com.svg"
alt="University Mail Icon"
className="w-6 h-5"

<GoogleLogin
onSuccess={handleGoogleSuccess}
onError={() => setError("Google login failed")}
useOneTap
/>
<span>Continue with University mail</span>
</button>


<div className="flex items-center mb-4">
<hr className="flex-grow border-gray-300" />
Expand Down
Loading