| # from werkzeug.security import generate_password_hash, check_password_hash | |
| # from pydantic import BaseModel | |
| # from typing import List | |
| # from .mongo import users_collection | |
| # from utils.auth import create_access_token, create_refresh_token, decode_token | |
| # from fastapi import HTTPException, Response , Request | |
| # from dotenv import load_dotenv | |
| # from bson import ObjectId | |
| # import httpx | |
| # import os | |
| # load_dotenv() | |
| # class User(BaseModel): | |
| # username: str | |
| # password: str | |
| # captcha_token: str | |
| # class Token(BaseModel): | |
| # access_token: str | |
| # token_type: str | |
| # favorites: List[str] | |
| # async def verify_captcha(token: str): | |
| # """Verify Cloudflare Turnstile CAPTCHA.""" | |
| # secret_key = os.getenv("CAPTCHA_SECRET_KEY") | |
| # verify_url = "https://challenges.cloudflare.com/turnstile/v0/siteverify" | |
| # async with httpx.AsyncClient() as client: | |
| # response = await client.post( | |
| # verify_url, | |
| # data={"secret": secret_key, "response": token} | |
| # ) | |
| # result = response.json() | |
| # if not result.get("success"): | |
| # raise HTTPException( | |
| # status_code=400, | |
| # detail="CAPTCHA verification failed" | |
| # ) | |
| # def set_auth_cookies(response: Response, access_token: str, refresh_token: str): | |
| # """Set secure auth cookies.""" | |
| # response.set_cookie( | |
| # key="access_token", | |
| # value=access_token, | |
| # httponly=True, | |
| # secure=False, | |
| # samesite="Lax" | |
| # ) | |
| # response.set_cookie( | |
| # key="refresh_token", | |
| # value=refresh_token, | |
| # httponly=True, | |
| # secure=False, | |
| # samesite="Lax" | |
| # ) | |
| # async def signup(user: User, response: Response): | |
| # if not user.username or not user.password: | |
| # raise HTTPException(status_code=400, detail="Username and password required") | |
| # await verify_captcha(user.captcha_token) | |
| # if users_collection.find_one({"username": user.username}): | |
| # raise HTTPException(status_code=400, detail="Username already exists") | |
| # hashed_password = generate_password_hash(user.password) | |
| # new_user = users_collection.insert_one({ | |
| # "username": user.username, | |
| # "password": hashed_password, | |
| # "favorites": [], | |
| # "refresh_token": None | |
| # }) | |
| # user_id = str(new_user.inserted_id) | |
| # token_data = {"username": user.username, "user_id": user_id} | |
| # access_token = create_access_token(token_data) | |
| # refresh_token = create_refresh_token(token_data) | |
| # users_collection.update_one( | |
| # {"_id": new_user.inserted_id}, | |
| # {"$set": {"refresh_token": refresh_token}} | |
| # ) | |
| # set_auth_cookies(response, access_token, refresh_token) | |
| # return { | |
| # "access_token": access_token, | |
| # "favorites": [] | |
| # } | |
| # async def login(user: User, response: Response): | |
| # if not user.username or not user.password: | |
| # raise HTTPException(status_code=400, detail="Username and password required") | |
| # await verify_captcha(user.captcha_token) | |
| # stored_user = users_collection.find_one({"username": user.username}) | |
| # if not stored_user or not check_password_hash(stored_user["password"], user.password): | |
| # raise HTTPException(status_code=401, detail="Invalid username or password") | |
| # user_id = str(stored_user["_id"]) | |
| # favorites = stored_user.get("favorites", []) | |
| # token_data = {"username": user.username, "user_id": user_id} | |
| # access_token = create_access_token(token_data) | |
| # refresh_token = create_refresh_token(token_data) | |
| # users_collection.update_one( | |
| # {"_id": stored_user["_id"]}, | |
| # {"$set": {"refresh_token": refresh_token}} | |
| # ) | |
| # set_auth_cookies(response, access_token, refresh_token) | |
| # return { | |
| # "access_token": access_token, | |
| # "favorites": favorites | |
| # } | |
| # async def refresh_token(request: Request, response: Response): | |
| # refresh_token_cookie = request.cookies.get("refresh_token") | |
| # if not refresh_token_cookie: | |
| # raise HTTPException(status_code=401, detail="Missing refresh token") | |
| # payload = decode_token(refresh_token_cookie) | |
| # user = users_collection.find_one({ | |
| # "_id": ObjectId(payload.get("user_id")) | |
| # }) | |
| # if not user or user.get("refresh_token") != refresh_token_cookie: | |
| # raise HTTPException(status_code=401, detail="Invalid refresh token") | |
| # token_data = { | |
| # "username": payload["username"], | |
| # "user_id": payload["user_id"] | |
| # } | |
| # new_access_token = create_access_token(token_data) | |
| # response.set_cookie( | |
| # key="access_token", | |
| # value=new_access_token, | |
| # httponly=True, | |
| # secure=False, | |
| # samesite="Lax" | |
| # ) | |
| # return {"access_token": new_access_token} | |
| # async def get_current_user(request: Request): | |
| # """ | |
| # Return the currently authenticated user. | |
| # Reads access_token from httpOnly cookie. | |
| # """ | |
| # access_token = request.cookies.get("access_token") | |
| # if not access_token: | |
| # raise HTTPException(status_code=401, detail="Not authenticated") | |
| # try: | |
| # payload = decode_token(access_token) | |
| # except Exception: | |
| # raise HTTPException(status_code=401, detail="Invalid or expired token") | |
| # user_id = payload.get("user_id") | |
| # if not user_id: | |
| # raise HTTPException(status_code=401, detail="Invalid token payload") | |
| # user_doc = users_collection.find_one({"_id": ObjectId(user_id)}) | |
| # if not user_doc: | |
| # raise HTTPException(status_code=404, detail="User not found") | |
| # return { | |
| # "user": { | |
| # "user_id": str(user_doc["_id"]), | |
| # "username": user_doc["username"], | |
| # "favorites": user_doc.get("favorites", []) | |
| # } | |
| # } | |
| # async def logout(response: Response, request): | |
| # refresh_token = request.cookies.get("refresh_token") | |
| # if refresh_token: | |
| # payload = decode_token(refresh_token) | |
| # users_collection.update_one( | |
| # {"_id": payload.get("user_id")}, | |
| # {"$set": {"refresh_token": None}} | |
| # ) | |
| # response.delete_cookie("access_token") | |
| # response.delete_cookie("refresh_token") | |
| # return {"message": "Logged out successfully"} |