📄 auth.py 4,964 bytes Sunday 12:32 📋 Raw

"""
Authentication API Endpoints
POST /api/v1/auth/login - Returns JWT access token
"""
from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from pydantic import BaseModel
from sqlalchemy.orm import Session
from typing import Optional

from app.database import get_db
from app.models import User
from app.auth import hash_password, verify_password, create_access_token, get_current_user

router = APIRouter(tags=["auth"])

============== REQUEST/RESPONSE SCHEMAS ==============

class LoginRequest(BaseModel):
"""Login request with email and password"""
email: str
password: str

class LoginResponse(BaseModel):
"""Login response with JWT token"""
access_token: str
token_type: str = "bearer"
role: str
school_id: str
assigned_sports: list = []

class TokenRefreshRequest(BaseModel):
"""Token refresh request (placeholder for future use)"""
token: str

============== ENDPOINTS ==============

@router.post("/login", response_model=LoginResponse)
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_db)
):
"""
Authenticate user and return JWT access token

Uses OAuth2PasswordRequestForm for standard form-based auth.
username field should contain email address.

Returns:
    access_token: JWT token for Authorization header
    token_type: "bearer"
    role: User's role (at/coach/parent/admin)
    school_id: User's school affiliation
"""
# Find user by email (username field contains email)
user = db.query(User).filter(User.email == form_data.username).first()

if not user:
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid email or password",
        headers={"WWW-Authenticate": "Bearer"},
    )

# Verify password
if not verify_password(form_data.password, user.hashed_password):
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid email or password",
        headers={"WWW-Authenticate": "Bearer"},
    )

# Check if user is active
if not user.is_active:
    raise HTTPException(
        status_code=status.HTTP_403_FORBIDDEN,
        detail="User account is inactive"
    )

# Create JWT token
access_token = create_access_token(
    user_id=user.id,
    role=user.role,
    school_id=user.school_id,
    assigned_sports=user.assigned_sports or []
)

return LoginResponse(
    access_token=access_token,
    token_type="bearer",
    role=user.role,
    school_id=user.school_id,
    assigned_sports=user.assigned_sports or []
)

@router.post("/login/json", response_model=LoginResponse)
async def login_json(
login_data: LoginRequest,
db: Session = Depends(get_db)
):
"""
Authenticate user and return JWT access token (JSON variant)

Alternative to form-based auth for JSON clients.
Same functionality as /login.
"""
# Find user by email
user = db.query(User).filter(User.email == login_data.email).first()

if not user:
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid email or password",
        headers={"WWW-Authenticate": "Bearer"},
    )

# Verify password
if not verify_password(login_data.password, user.hashed_password):
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid email or password",
        headers={"WWW-Authenticate": "Bearer"},
    )

# Check if user is active
if not user.is_active:
    raise HTTPException(
        status_code=status.HTTP_403_FORBIDDEN,
        detail="User account is inactive"
    )

# Create JWT token
access_token = create_access_token(
    user_id=user.id,
    role=user.role,
    school_id=user.school_id,
    assigned_sports=user.assigned_sports or []
)

return LoginResponse(
    access_token=access_token,
    token_type="bearer",
    role=user.role,
    school_id=user.school_id,
    assigned_sports=user.assigned_sports or []
)

@router.get("/me")
async def get_current_user_info(
current_user: User = Depends(get_current_user),
):
"""Get current authenticated user's info"""
return {
"id": current_user.id,
"email": current_user.email,
"role": current_user.role,
"school_id": current_user.school_id,
"first_name": current_user.first_name,
"last_name": current_user.last_name,
"is_active": current_user.is_active,
"assigned_sports": current_user.assigned_sports or [],
"assigned_teams": current_user.assigned_teams or [],
}