Files
AudiOhm/backend/app/api/v1/auth.py
T
root a89c7894cf Initial commit: AudiOhm - Alternative Spotify avec streaming YouTube
Backend:
- FastAPI avec PostgreSQL et Redis
- Authentification JWT complète
- API REST pour musique, playlists, recherche
- Streaming audio via yt-dlp
- SQLAlchemy 2.0 async

Frontend:
- Flutter avec thème néon cyberpunk
- State management Riverpod
- Layout adaptatif desktop/mobile
- Lecteur audio avec mini-player

Infrastructure:
- Docker Compose (PostgreSQL + Redis)
- Scripts d'installation automatisés
- Scripts de build pour exécutables

Fichiers ajoutés:
- BUILD_CLIENT_*.bat/sh: Scripts de compilation
- BUILD_CLIENT_README.md: Documentation compilation
- CHECK_FLUTTER.sh: Vérificateur d'environnement
- requirements.txt mis à jour pour Python 3.13
- Modèles SQLAlchemy corrigés (metadata -> extra_metadata)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 20:08:36 +00:00

179 lines
4.7 KiB
Python

"""Authentication API routes."""
from fastapi import APIRouter, HTTPException, status
from app.api.dependencies import AuthServiceDep, CurrentUser, DBSession
from app.schemas.auth import (
LoginRequest,
RefreshTokenRequest,
Token,
UserCreate,
UserResponse,
UserUpdate,
)
from app.services.auth_service import AuthService
router = APIRouter(prefix="/auth", tags=["authentication"])
@router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def register(
user_data: UserCreate,
auth_service: AuthServiceDep,
):
"""
Register a new user.
- **email**: Valid email address
- **username**: 3-50 characters, unique
- **password**: Min 8 characters
- **display_name**: Optional display name
"""
try:
user = await auth_service.register(
email=user_data.email,
username=user_data.username,
password=user_data.password,
display_name=user_data.display_name,
)
return UserResponse.model_validate(user)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
@router.post("/login", response_model=Token)
async def login(
credentials: LoginRequest,
auth_service: AuthServiceDep,
):
"""
Login with email and password.
Returns access and refresh tokens.
"""
try:
user = await auth_service.login(
email=credentials.email,
password=credentials.password,
)
access_token, refresh_token = auth_service.create_tokens(user.id)
return Token(
access_token=access_token,
refresh_token=refresh_token,
expires_in=15 * 60, # 15 minutes
)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=str(e),
headers={"WWW-Authenticate": "Bearer"},
)
@router.post("/refresh", response_model=Token)
async def refresh_token(
token_data: RefreshTokenRequest,
auth_service: AuthServiceDep,
):
"""
Refresh access token using refresh token.
Returns new access and refresh tokens.
"""
from app.core.security import decode_token
try:
payload = decode_token(token_data.refresh_token)
user_id = payload.get("sub")
token_type = payload.get("type")
if user_id is None or token_type != "refresh":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token",
)
# Verify user still exists
user = await auth_service.get_user_by_id(user_id)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found",
)
# Create new tokens
access_token, refresh_token = auth_service.create_tokens(user.id)
return Token(
access_token=access_token,
refresh_token=refresh_token,
expires_in=15 * 60,
)
except Exception:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token",
)
@router.get("/me", response_model=UserResponse)
async def get_current_user(
current_user: CurrentUser,
):
"""
Get current authenticated user profile.
Requires authentication.
"""
return UserResponse.model_validate(current_user)
@router.put("/me", response_model=UserResponse)
async def update_current_user(
user_data: UserUpdate,
current_user: CurrentUser,
auth_service: AuthServiceDep,
):
"""
Update current user profile.
Requires authentication.
"""
try:
updated_user = await auth_service.update_user(
user_id=current_user.id,
display_name=user_data.display_name,
avatar_url=user_data.avatar_url,
date_of_birth=user_data.date_of_birth,
country=user_data.country,
)
return UserResponse.model_validate(updated_user)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e),
)
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
async def logout(
current_user: CurrentUser,
):
"""
Logout current user.
In a stateless JWT setup, this is mainly for client-side cleanup.
The token will expire automatically.
Requires authentication.
"""
# In production, you might want to:
# - Add token to blacklist (Redis)
# - Remove refresh token from database
# - Log the logout event
return None