| import os |
| from typing import Any, Dict, Optional, Tuple |
|
|
| import requests |
| from flask import Blueprint, jsonify, request |
|
|
| from backend import db |
|
|
|
|
| auth_blueprint = Blueprint("auth", __name__) |
|
|
|
|
| SUPABASE_URL = os.getenv("SUPABASE_URL") |
| SUPABASE_ANON_KEY = os.getenv("SUPABASE_ANON_KEY") |
| SUPABASE_SERVICE_ROLE_KEY = os.getenv("SUPABASE_SERVICE_ROLE_KEY") |
|
|
| if not SUPABASE_URL or not SUPABASE_ANON_KEY or not SUPABASE_SERVICE_ROLE_KEY: |
| raise RuntimeError("Supabase credentials (URL, ANON key, service role key) must be configured.") |
|
|
| AUTH_BASE = f"{SUPABASE_URL}/auth/v1" |
|
|
|
|
| def _extract_token_from_header() -> Optional[str]: |
| auth_header = request.headers.get("Authorization", "") |
| if not auth_header.startswith("Bearer "): |
| return None |
| return auth_header.split(" ", maxsplit=1)[1].strip() |
|
|
|
|
| def _fetch_supabase_user(accessToken: str) -> Tuple[Optional[Dict[str, Any]], Optional[Tuple[str, int]]]: |
| """Validate the Supabase access token and return the user payload.""" |
| response = requests.get( |
| f"{AUTH_BASE}/user", |
| headers={ |
| "Authorization": f"Bearer {accessToken}", |
| "apikey": SUPABASE_ANON_KEY, |
| }, |
| timeout=10, |
| ) |
| if response.status_code != 200: |
| return None, ("Invalid Supabase access token.", 401) |
| return response.json(), None |
|
|
|
|
| def verify_request_token() -> Tuple[Optional[Dict[str, Any]], Optional[Tuple[str, int]]]: |
| token = _extract_token_from_header() |
| if not token: |
| return None, ("Authorization header missing or invalid.", 401) |
| return _fetch_supabase_user(token) |
|
|
|
|
| @auth_blueprint.route("/session", methods=["GET"]) |
| def session_info(): |
| """Return the Supabase-authenticated user profile.""" |
| user_payload, error = verify_request_token() |
| if error: |
| message, status = error |
| return jsonify({"error": message}), status |
|
|
| user_id = user_payload.get("id") |
| profile = db.get_user_profile(user_id) |
| return jsonify({"user": user_payload, "profile": profile}) |
|
|