Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| """ | |
| Data deletion endpoints for OAuth provider compliance (Facebook, etc.) | |
| """ | |
| from fastapi import APIRouter, Request, HTTPException, Depends | |
| from pydantic import BaseModel | |
| from sqlalchemy.orm import Session | |
| from loguru import logger | |
| import hmac | |
| import hashlib | |
| import base64 | |
| import json | |
| import os | |
| from datetime import datetime | |
| from api.database import get_db | |
| from api.models import User | |
| router = APIRouter() | |
| class DataDeletionRequest(BaseModel): | |
| """Data deletion request from OAuth provider""" | |
| signed_request: str # Facebook's signed request | |
| class DataDeletionResponse(BaseModel): | |
| """Response to data deletion request""" | |
| url: str | |
| confirmation_code: str | |
| def parse_signed_request(signed_request: str, app_secret: str) -> dict: | |
| """ | |
| Parse and validate Facebook's signed_request parameter. | |
| Format: base64url(signature).base64url(payload) | |
| """ | |
| try: | |
| encoded_sig, payload = signed_request.split('.', 1) | |
| # Decode signature | |
| sig = base64.urlsafe_b64decode(encoded_sig + '==') # Add padding | |
| # Decode payload | |
| data = json.loads(base64.urlsafe_b64decode(payload + '==')) | |
| # Verify signature | |
| expected_sig = hmac.new( | |
| app_secret.encode('utf-8'), | |
| msg=payload.encode('utf-8'), | |
| digestmod=hashlib.sha256 | |
| ).digest() | |
| if sig != expected_sig: | |
| raise ValueError("Invalid signature") | |
| return data | |
| except Exception as e: | |
| logger.error(f"Error parsing signed_request: {e}") | |
| raise HTTPException(status_code=400, detail="Invalid signed request") | |
| async def facebook_data_deletion_callback( | |
| request: Request, | |
| db: Session = Depends(get_db) | |
| ): | |
| """ | |
| Facebook Data Deletion Request Callback | |
| Facebook requires apps to provide a data deletion callback URL. | |
| This endpoint receives deletion requests and processes them. | |
| Docs: https://developers.facebook.com/docs/development/create-an-app/app-dashboard/data-deletion-callback | |
| """ | |
| # Get signed_request from form data | |
| form = await request.form() | |
| signed_request = form.get("signed_request") | |
| if not signed_request: | |
| raise HTTPException(status_code=400, detail="Missing signed_request") | |
| # Get Facebook app secret | |
| app_secret = os.getenv("FACEBOOK_APP_SECRET") | |
| if not app_secret: | |
| logger.error("FACEBOOK_APP_SECRET not configured") | |
| raise HTTPException(status_code=500, detail="Server configuration error") | |
| # Parse and validate signed request | |
| data = parse_signed_request(signed_request, app_secret) | |
| user_id = data.get("user_id") # Facebook user ID | |
| if not user_id: | |
| raise HTTPException(status_code=400, detail="Missing user_id in signed request") | |
| logger.info(f"Data deletion request from Facebook user: {user_id}") | |
| # Find user by OAuth provider ID | |
| user = db.query(User).filter( | |
| User.oauth_provider == "facebook", | |
| User.oauth_id == user_id | |
| ).first() | |
| if user: | |
| # Delete user data | |
| logger.info(f"Deleting user account: {user.email} (Facebook ID: {user_id})") | |
| db.delete(user) | |
| db.commit() | |
| status = "deleted" | |
| else: | |
| logger.info(f"No user found for Facebook ID: {user_id}") | |
| status = "not_found" | |
| # Generate confirmation code (can be used for tracking) | |
| confirmation_code = f"DEL-{user_id}-{int(datetime.now().timestamp())}" | |
| # Return deletion status URL | |
| # Users will be redirected here to see status | |
| base_url = os.getenv("FRONTEND_URL", "https://www.communityone.com") | |
| status_url = f"{base_url}/data-deletion-status?code={confirmation_code}&status={status}" | |
| logger.info(f"Data deletion request processed: {confirmation_code}") | |
| return DataDeletionResponse( | |
| url=status_url, | |
| confirmation_code=confirmation_code | |
| ) | |
| async def data_deletion_status(code: str = None, status: str = None): | |
| """ | |
| Data deletion status page (informational) | |
| Users are redirected here after Facebook processes their deletion request. | |
| """ | |
| return { | |
| "confirmation_code": code, | |
| "status": status, | |
| "message": "Your data deletion request has been processed." if status == "deleted" else "No account found.", | |
| "timestamp": datetime.now().isoformat() | |
| } | |