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
50 changes: 50 additions & 0 deletions backend/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,54 @@
# ──────────────────────────────────────────────
#
# ──────────────────────────────────────────────

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from supabase import create_client
import os

# Create a router for auth endpoints
router = APIRouter(prefix="/api/v1/auth", tags=["auth"])

# Request body model — expects a refresh_token string
class RefreshRequest(BaseModel):
refresh_token: str

@router.post("/refresh")
async def refresh_session(body: RefreshRequest):
"""
Exchange a Supabase refresh_token for a new access_token + refresh_token.
Called automatically by the frontend when a 401 Unauthorized error occurs.
"""
try:
# Create a Supabase client using environment variables
supabase_url = os.environ.get("SUPABASE_URL")
supabase_key = os.environ.get("SUPABASE_KEY")

if not supabase_url or not supabase_key:
raise HTTPException(status_code=500, detail="Supabase environment variables not configured.")

supabase = create_client(supabase_url, supabase_key)

# Ask Supabase to give us a fresh session using the refresh token
response = supabase.auth.refresh_session(body.refresh_token)

# If Supabase didn't return a session, something is wrong
if not response or not response.session:
raise HTTPException(status_code=401, detail="Invalid or expired refresh token")

# Return the new tokens to the frontend
return {
"access_token": response.session.access_token,
"refresh_token": response.session.refresh_token,
}

except HTTPException:
# Re-raise HTTP exceptions as-is
raise
except Exception as e:
# Any other error means the refresh failed
raise HTTPException(status_code=401, detail=f"Token refresh failed: {str(e)}")
Comment on lines +49 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid exposing internal error details in the response.

Including str(e) in the HTTP response could leak sensitive information about the backend (e.g., database connection strings, internal paths, or Supabase error internals). Return a generic message instead.

🛡️ Proposed fix
     except Exception as e:
         # Any other error means the refresh failed
-        raise HTTPException(status_code=401, detail=f"Token refresh failed: {str(e)}")
+        print(f"Token refresh failed: {e}")  # Log internally for debugging
+        raise HTTPException(status_code=401, detail="Token refresh failed")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/auth.py` around lines 46 - 48, The except block that currently does
"except Exception as e: raise HTTPException(status_code=401, detail=f'Token
refresh failed: {str(e)}')" should be changed to avoid leaking internal error
text: replace the HTTPException detail with a generic message like "Token
refresh failed" or "Unauthorized", and log the original exception internally
using an application logger (e.g., logger.exception or logger.error with the
exception) before raising HTTPException; keep the status_code=401 and continue
to raise HTTPException but do not include str(e) in the response.

import uuid
from fastapi import HTTPException, Header
from supabase import create_client, Client
Expand Down
4 changes: 3 additions & 1 deletion backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime, timezone
from contextlib import asynccontextmanager
from typing import Optional
from auth import router as auth_router
from auth import get_current_user, get_google_oauth_url, exchange_code_for_session
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
Expand Down Expand Up @@ -95,7 +96,8 @@ async def lifespan(app: FastAPI):

from fastapi import FastAPI

app = FastAPI(title="FreshScan AI", version="1.1.0", lifespan=lifespan)
app = FastAPI(title="FreshScan AI", version="1.1.0", lifespan=lifespan)
app.include_router(auth_router)

_cors_origins = (
["*"]
Expand Down
Loading
Loading