Barclays-banner-builder/backend/app/api/auth.py
Vadym Samoilenko 3305a5e8f4 Disable auth for testing (AUTH_BYPASS=True)
Frontend: ProtectedRoute always passes through.
Backend: get_current_user returns first admin user when AUTH_BYPASS=True.
Revert both files before go-live.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:13:31 +01:00

97 lines
3.1 KiB
Python

from datetime import datetime, timedelta, timezone
import bcrypt
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import get_settings
from app.database import get_db
from app.models.user import User
router = APIRouter()
# AUTH_BYPASS=True — remove for production
AUTH_BYPASS = True
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token", auto_error=not AUTH_BYPASS)
def hash_password(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
def verify_password(plain: str, hashed: str) -> bool:
return bcrypt.checkpw(plain.encode(), hashed.encode())
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
user_id: str | None = None
def create_access_token(data: dict) -> str:
settings = get_settings()
payload = data.copy()
payload["exp"] = datetime.now(timezone.utc) + timedelta(
minutes=settings.access_token_expire_minutes
)
return jwt.encode(payload, settings.secret_key, algorithm=settings.jwt_algorithm)
async def get_current_user(
token: str | None = Depends(oauth2_scheme),
db: AsyncSession = Depends(get_db),
) -> User:
# AUTH_BYPASS — skip token validation, return first admin user
if AUTH_BYPASS:
result = await db.execute(select(User).where(User.role == "admin").limit(1))
user = result.scalar_one_or_none()
if user:
return user
settings = get_settings()
credentials_exc = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Bearer"},
)
if not token:
raise credentials_exc
try:
payload = jwt.decode(token, settings.secret_key, algorithms=[settings.jwt_algorithm])
user_id = payload.get("sub")
if user_id is None:
raise credentials_exc
except JWTError:
raise credentials_exc
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user is None:
raise credentials_exc
return user
@router.post("/token", response_model=Token)
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(User).where(User.email == form_data.username))
user = result.scalar_one_or_none()
if not user or not verify_password(form_data.password, user.hashed_password):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
token = create_access_token({"sub": str(user.id)})
return {"access_token": token, "token_type": "bearer"}
@router.get("/me")
async def me(current_user: User = Depends(get_current_user)):
return {"id": str(current_user.id), "email": current_user.email, "role": current_user.role}