| import os |
| import json |
| import requests |
| import firebase_admin |
| from firebase_admin import credentials, auth as firebase_auth |
|
|
| FIREBASE_API_KEY = os.environ.get("FIREBASE_API_KEY", "") |
| FIREBASE_AUTH_REST = ( |
| "https://identitytoolkit.googleapis.com/v1/accounts" |
| ) |
|
|
| |
| def init_firebase(): |
| if firebase_admin._apps: |
| return |
| sa_json = os.environ.get("FIREBASE_SERVICE_ACCOUNT_JSON") |
| if not sa_json: |
| print("β οΈ No Firebase service account - auth disabled") |
| return |
| try: |
| cred = credentials.Certificate(json.loads(sa_json)) |
| firebase_admin.initialize_app(cred) |
| print("β
Firebase Admin initialized") |
| except Exception as e: |
| print(f"β οΈ Firebase init error: {e}") |
|
|
| init_firebase() |
|
|
| |
| def email_signup(name: str, email: str, password: str) -> dict: |
| r = requests.post( |
| f"{FIREBASE_AUTH_REST}:signUp?key={FIREBASE_API_KEY}", |
| json={ |
| "email": email, |
| "password": password, |
| "returnSecureToken": True |
| }, |
| timeout=10 |
| ) |
| data = r.json() |
| if "idToken" not in data: |
| msg = data.get("error", {}).get("message", "Signup failed") |
| raise ValueError(msg) |
| |
| requests.post( |
| f"{FIREBASE_AUTH_REST}:update?key={FIREBASE_API_KEY}", |
| json={ |
| "idToken": data["idToken"], |
| "displayName": name |
| }, |
| timeout=10 |
| ) |
| return { |
| "uid": data["localId"], |
| "email": email, |
| "name": name, |
| "avatar": "", |
| "token": data["idToken"] |
| } |
|
|
| def email_login(email: str, password: str) -> dict: |
| r = requests.post( |
| f"{FIREBASE_AUTH_REST}:signInWithPassword" |
| f"?key={FIREBASE_API_KEY}", |
| json={ |
| "email": email, |
| "password": password, |
| "returnSecureToken": True |
| }, |
| timeout=10 |
| ) |
| data = r.json() |
| if "idToken" not in data: |
| msg = data.get("error", {}).get("message", "Login failed") |
| raise ValueError(msg) |
| return { |
| "uid": data["localId"], |
| "email": email, |
| "name": data.get("displayName", email.split("@")[0]), |
| "avatar": data.get("photoUrl", ""), |
| "token": data["idToken"] |
| } |
|
|
| def forgot_password(email: str): |
| r = requests.post( |
| f"{FIREBASE_AUTH_REST}:sendOobCode" |
| f"?key={FIREBASE_API_KEY}", |
| json={"requestType": "PASSWORD_RESET", "email": email}, |
| timeout=10 |
| ) |
| data = r.json() |
| if "email" not in data: |
| raise ValueError("Could not send reset email") |
|
|
| |
| def get_google_oauth_url(app_url: str) -> str: |
| client_id = os.environ.get("GOOGLE_CLIENT_ID", "") |
| return ( |
| "https://accounts.google.com/o/oauth2/v2/auth" |
| f"?client_id={client_id}" |
| f"&redirect_uri={app_url}" |
| "&response_type=code" |
| "&scope=openid%20email%20profile" |
| "&state=google" |
| "&prompt=select_account" |
| ) |
|
|
| def google_callback(code: str, app_url: str) -> dict: |
| client_id = os.environ.get("GOOGLE_CLIENT_ID", "") |
| client_secret = os.environ.get("GOOGLE_CLIENT_SECRET", "") |
| |
| token_r = requests.post( |
| "https://oauth2.googleapis.com/token", |
| data={ |
| "code": code, |
| "client_id": client_id, |
| "client_secret": client_secret, |
| "redirect_uri": app_url, |
| "grant_type": "authorization_code" |
| }, |
| timeout=10 |
| ) |
| tokens = token_r.json() |
| google_id_token = tokens.get("id_token") |
| if not google_id_token: |
| raise ValueError("Google token exchange failed") |
| |
| fb_r = requests.post( |
| f"https://identitytoolkit.googleapis.com/v1/accounts" |
| f":signInWithIdp?key={FIREBASE_API_KEY}", |
| json={ |
| "postBody": ( |
| f"id_token={google_id_token}" |
| "&providerId=google.com" |
| ), |
| "requestUri": app_url, |
| "returnIdpCredential": True, |
| "returnSecureToken": True |
| }, |
| timeout=10 |
| ) |
| fb_data = fb_r.json() |
| if "idToken" not in fb_data: |
| err_msg = fb_data.get("error", {}).get("message", "Unknown Firebase error") |
| raise ValueError(f"Firebase Google sign-in failed: {err_msg}") |
| return { |
| "uid": fb_data["localId"], |
| "email": fb_data.get("email", ""), |
| "name": fb_data.get("displayName", "Student"), |
| "avatar": fb_data.get("photoUrl", ""), |
| "token": fb_data["idToken"] |
| } |
|
|
| |
| def get_github_oauth_url(app_url: str) -> str: |
| client_id = os.environ.get("GITHUB_CLIENT_ID", "") |
| return ( |
| "https://github.com/login/oauth/authorize" |
| f"?client_id={client_id}" |
| f"&redirect_uri={app_url}" |
| "&scope=user:email" |
| "&state=github" |
| ) |
|
|
| def github_callback(code: str, app_url: str) -> dict: |
| client_id = os.environ.get("GITHUB_CLIENT_ID", "") |
| client_secret = os.environ.get("GITHUB_CLIENT_SECRET", "") |
| |
| token_r = requests.post( |
| "https://github.com/login/oauth/access_token", |
| headers={"Accept": "application/json"}, |
| data={ |
| "client_id": client_id, |
| "client_secret": client_secret, |
| "code": code, |
| "redirect_uri": app_url |
| }, |
| timeout=10 |
| ) |
| token_data = token_r.json() |
| access_token = token_data.get("access_token") |
| if not access_token: |
| raise ValueError("GitHub token exchange failed") |
| |
| fb_r = requests.post( |
| f"https://identitytoolkit.googleapis.com/v1/accounts" |
| f":signInWithIdp?key={FIREBASE_API_KEY}", |
| json={ |
| "postBody": ( |
| f"access_token={access_token}" |
| "&providerId=github.com" |
| ), |
| "requestUri": app_url, |
| "returnIdpCredential": True, |
| "returnSecureToken": True |
| }, |
| timeout=10 |
| ) |
| fb_data = fb_r.json() |
| if "idToken" not in fb_data: |
| raise ValueError("Firebase GitHub sign-in failed") |
| return { |
| "uid": fb_data["localId"], |
| "email": fb_data.get("email", ""), |
| "name": fb_data.get("displayName", "Student"), |
| "avatar": fb_data.get("photoUrl", ""), |
| "token": fb_data["idToken"] |
| } |
|
|