Files
freetable/backend/app/routers/auth.py
T

117 lines
3.7 KiB
Python
Raw Normal View History

2026-06-09 23:32:41 +00:00
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
2026-06-09 23:32:41 +00:00
from sqlalchemy.orm import Session
from app.database import get_db
from app.models.user import User
from app.schemas.auth import LoginRequest, Token
from app.schemas.user import UserCreate, UserRead
from app.utils.security import (
verify_password,
get_password_hash,
create_access_token,
decode_access_token,
)
router = APIRouter()
security = HTTPBearer()
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(get_db),
) -> User:
"""Dependency to get the current authenticated user."""
token = credentials.credentials
payload = decode_access_token(token)
if payload is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token",
headers={"WWW-Authenticate": "Bearer"},
)
user_id = payload.get("sub")
if user_id is None:
2026-06-09 23:32:41 +00:00
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token payload",
headers={"WWW-Authenticate": "Bearer"},
2026-06-09 23:32:41 +00:00
)
user = db.query(User).filter(User.id == int(user_id)).first()
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found",
headers={"WWW-Authenticate": "Bearer"},
)
return user
@router.post(
"/auth/register", response_model=UserRead, status_code=status.HTTP_201_CREATED
)
def register(user_data: UserCreate, db: Session = Depends(get_db)):
"""Register a new user."""
# Check if email already exists
existing_user = db.query(User).filter(User.email == user_data.email).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered"
)
# Create new user
hashed_password = get_password_hash(user_data.password)
new_user = User(
email=user_data.email,
password_hash=hashed_password,
name=user_data.name,
role="member",
2026-06-09 23:32:41 +00:00
)
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user
@router.post("/auth/login", response_model=Token)
def login(login_data: LoginRequest, db: Session = Depends(get_db)):
"""Login and return JWT token."""
user = db.query(User).filter(User.email == login_data.email).first()
if not user or not verify_password(login_data.password, user.password_hash):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid email or password"
)
access_token = create_access_token(data={"sub": str(user.id), "email": user.email})
return Token(access_token=access_token)
@router.post("/auth/logout")
def logout(current_user: User = Depends(get_current_user)):
"""Logout (client should discard the token)."""
return {"message": "Logged out successfully"}
@router.get("/auth/me", response_model=UserRead)
def get_me(current_user: User = Depends(get_current_user)):
"""Get current user info."""
return current_user
@router.put("/auth/password")
def change_password(
current_password: str,
new_password: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
"""Change password."""
if not verify_password(current_password, current_user.password_hash):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Current password is incorrect",
)
current_user.password_hash = get_password_hash(new_password)
db.commit()
return {"message": "Password changed successfully"}