import os import bcrypt from datetime import datetime, timedelta, timezone from typing import Optional from jose import JWTError, jwt from fastapi import Depends, HTTPException, Request, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials # ponytail: fallback inseguro — set RIPS_SECRET_KEY en producción SECRET_KEY = os.environ.get("RIPS_SECRET_KEY", "rips-manager-secret-key-change-in-production") ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_HOURS = 12 security = HTTPBearer(auto_error=False) def hash_password(password: str) -> str: return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() def verify_password(password: str, password_hash: str) -> bool: return bcrypt.checkpw(password.encode(), password_hash.encode()) def create_token(user_id: int, username: str) -> str: payload = { "user_id": user_id, "username": username, "exp": datetime.now(timezone.utc) + timedelta(hours=ACCESS_TOKEN_EXPIRE_HOURS), } return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM) def decode_token(token: str) -> Optional[dict]: try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) return payload except JWTError: return None def get_current_user(request: Request, credentials: HTTPAuthorizationCredentials = Depends(security)): if hasattr(request.state, "user") and request.state.user: return request.state.user if credentials is None: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") payload = decode_token(credentials.credentials) if payload is None: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") return payload def optional_user(request: Request, credentials: HTTPAuthorizationCredentials = Depends(security)): if hasattr(request.state, "user") and request.state.user: return request.state.user if credentials is None: return None return decode_token(credentials.credentials)