Spaces:
Sleeping
Sleeping
Download auth.py from rairo/chilltrack-api: direct link, hf CLI and curl.
- Browser
- Download file 3.23 kB
-
https://huggingface.co/spaces/rairo/chilltrack-api/resolve/main/auth.py
- Command line
-
hf download hf://spaces/rairo/chilltrack-api/auth.py
-
curl -L -o auth.py https://huggingface.co/spaces/rairo/chilltrack-api/resolve/main/auth.py
3.23 kB
| """ | |
| ChillTrack API — Firebase auth helpers. | |
| Auth pattern follows RentNow-Admin-API/app.py and NeoFix-API/main.py: | |
| - Firebase service account JSON lives in the `FIREBASE` env var (json.loads it). | |
| - Firestore is the datastore (unlike the Realtime-DB repos), so init mirrors | |
| RentNow's firestore.client(database_id=os.environ.get("FIRESTORE_DB_NAME", "(default)")). | |
| - Firebase ID token in `Authorization: Bearer <token>`. | |
| - 401 = missing/invalid token, 403 = signed in but wrong role. | |
| """ | |
| import os | |
| import json | |
| from functools import wraps | |
| from flask import request, jsonify | |
| import firebase_admin | |
| from firebase_admin import credentials, auth, firestore | |
| AUDIT_COLLECTION = "ctk_audit" | |
| def init_firebase(): | |
| """Initializes the Firebase Admin app exactly once and returns the Firestore client.""" | |
| if firebase_admin._apps: | |
| return firestore.client(database_id=os.environ.get("FIRESTORE_DB_NAME", "(default)")) | |
| raw = os.environ.get("FIREBASE") | |
| if not raw: | |
| print("FATAL: The 'FIREBASE' environment variable is not set.") | |
| exit(1) | |
| try: | |
| cred_json = json.loads(raw) | |
| cred = credentials.Certificate(cred_json) | |
| storage_bucket = os.environ.get("FIREBASE_STORAGE_BUCKET", "") | |
| firebase_admin.initialize_app(cred, {"storageBucket": storage_bucket} if storage_bucket else {}) | |
| print("Firebase Admin SDK initialized successfully.") | |
| except Exception as e: | |
| print(f"FATAL: Error initializing Firebase: {e}") | |
| exit(1) | |
| return firestore.client(database_id=os.environ.get("FIRESTORE_DB_NAME", "(default)")) | |
| def require_auth(fn): | |
| """Verifies the Firebase ID token and attaches the decoded token to request.user.""" | |
| def wrapper(*args, **kwargs): | |
| header = request.headers.get("Authorization", "") | |
| token = header[7:].strip() if header.startswith("Bearer ") else "" | |
| if not token: | |
| return jsonify({"error": "Missing bearer token."}), 401 | |
| try: | |
| decoded_token = auth.verify_id_token(token) | |
| except Exception as e: | |
| print(f"Token verification failed: {e}") | |
| return jsonify({"error": "Invalid or expired token."}), 401 | |
| request.user = decoded_token | |
| return fn(*args, **kwargs) | |
| return wrapper | |
| def require_role(role): | |
| """Decorator factory: require_auth, plus request.user['role'] must equal `role`.""" | |
| def decorator(fn): | |
| def wrapper(*args, **kwargs): | |
| if request.user.get("role") != role: | |
| return jsonify({"error": f"'{role}' access required."}), 403 | |
| return fn(*args, **kwargs) | |
| return wrapper | |
| return decorator | |
| def audit_log(db, actor, action, entity, entity_id, metadata=None): | |
| """Writes an audit trail doc. actor is {uid, email}. Never raises — logs and swallows.""" | |
| try: | |
| db.collection(AUDIT_COLLECTION).add({ | |
| "actor": actor, | |
| "action": action, | |
| "entity": entity, | |
| "entity_id": entity_id, | |
| "metadata": metadata or {}, | |
| "ts": firestore.SERVER_TIMESTAMP, | |
| }) | |
| except Exception as e: | |
| print(f"audit_log failed: {e}") | |