focusflow / backend /auth.py
SivaRohith69's picture
feat: surface exact firebase error message for google sign in
7904bd0
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"
)
# ── Firebase Admin Init ──────────────────────────────────────
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()
# ── Email / Password ─────────────────────────────────────────
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)
# Set display name
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")
# ── Google OAuth ─────────────────────────────────────────────
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", "")
# Exchange code for tokens
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")
# Sign in to Firebase with Google token
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"]
}
# ── GitHub OAuth ─────────────────────────────────────────────
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", "")
# Exchange code for access token
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")
# Sign in to Firebase with GitHub token
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"]
}