from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from jose import JWTError from sqlalchemy.orm import Session from app.auth.security import verify_password, create_access_token, create_refresh_token, decode_token from app.database import get_db from app.models.user import User, Role from app.schemas.auth import Token, LoginRequest from app.schemas.user import UserCreate, UserOut router = APIRouter(prefix="/auth", tags=["auth"]) def _authenticate(db: Session, username: str, password: str) -> User: user = db.query(User).filter( (User.username == username) | (User.email == username) ).first() if not user or not verify_password(password, user.password_hash): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials") if not user.is_active: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Account is disabled") return user @router.post("/login", response_model=Token) def login(form: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)): user = _authenticate(db, form.username, form.password) return Token( access_token=create_access_token(user.id, user.role.name), refresh_token=create_refresh_token(user.id), ) @router.post("/login/json", response_model=Token) def login_json(body: LoginRequest, db: Session = Depends(get_db)): user = _authenticate(db, body.username, body.password) return Token( access_token=create_access_token(user.id, user.role.name), refresh_token=create_refresh_token(user.id), ) @router.post("/refresh", response_model=Token) def refresh(refresh_token: str, db: Session = Depends(get_db)): try: payload = decode_token(refresh_token) if payload.get("type") != "refresh": raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token type") user = db.get(User, int(payload["sub"])) if not user or not user.is_active: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found") except JWTError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired refresh token") return Token( access_token=create_access_token(user.id, user.role.name), refresh_token=create_refresh_token(user.id), ) @router.post("/register", response_model=UserOut, status_code=status.HTTP_201_CREATED) def register(body: UserCreate, db: Session = Depends(get_db)): from app.auth.security import hash_password if db.query(User).filter(User.username == body.username).first(): raise HTTPException(status_code=400, detail="Username already taken") if db.query(User).filter(User.email == body.email).first(): raise HTTPException(status_code=400, detail="Email already registered") role = db.get(Role, body.role_id) or db.query(Role).filter(Role.name == "user").first() user = User( username=body.username, email=body.email, password_hash=hash_password(body.password), role_id=role.id, ) db.add(user) db.commit() db.refresh(user) return user