LogicGoInfotechSpaces commited on
Commit
f2895a9
·
1 Parent(s): 907476b

Add Firebase auth verification, /api/me endpoint, and API_DOC_FIREBASE.txt

Browse files
Files changed (3) hide show
  1. API_DOC_FIREBASE.txt +221 -0
  2. app.py +74 -2
  3. requirements.txt +1 -0
API_DOC_FIREBASE.txt ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Smile Changer API (with Firebase Authentication)
2
+
3
+ Base URL
4
+ - https://logicgoinfotechspaces-smile-changer.hf.space
5
+
6
+ Authentication
7
+ - Scheme: Bearer ID token (Firebase Authentication)
8
+ - Header: Authorization: Bearer <ID_TOKEN>
9
+ - How to obtain ID token:
10
+ 1) Sign-in REST (replace YOUR_WEB_API_KEY and credentials):
11
+ curl -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=YOUR_WEB_API_KEY" \
12
+ -H "Content-Type: application/json" \
13
+ -d '{"email":"you@example.com","password":"yourPassword","returnSecureToken":true}'
14
+ Copy the value of "idToken" from the JSON response and use as the Bearer token.
15
+ 2) Or sign-in from your Firebase web/app and call currentUser.getIdToken().
16
+ - Notes: idToken expires in ~1 hour; sign in again to refresh. Service account JSON is configured in the server via FIREBASE_CREDENTIALS_JSON.
17
+
18
+ Health (public)
19
+ - GET /api/health
20
+ - Curl:
21
+ curl https://logicgoinfotechspaces-smile-changer.hf.space/api/health
22
+ - Response: {"status":"ok"}
23
+
24
+ Auth check & user info
25
+ - GET /api/me
26
+ - Headers: Authorization: Bearer <ID_TOKEN>
27
+ - Curl:
28
+ curl -H "Authorization: Bearer <ID_TOKEN>" \
29
+ https://logicgoinfotechspaces-smile-changer.hf.space/api/me
30
+ - Response (example): {"mode":"firebase","uid":"...","claims":{"email":"..."}}
31
+
32
+ List supported attributes
33
+ - GET /api/attributes
34
+ - Headers: Authorization: Bearer <ID_TOKEN>
35
+ - Curl:
36
+ curl -H "Authorization: Bearer <ID_TOKEN>" \
37
+ https://logicgoinfotechspaces-smile-changer.hf.space/api/attributes
38
+ - Response example: {"Smile":{"internal":"fs_smiling","min":-10.0,"max":10.0}, ...}
39
+
40
+ Generic image edit
41
+ - POST /api/image-edit
42
+ - Headers: Authorization: Bearer <ID_TOKEN>
43
+ - Body (multipart/form-data):
44
+ - file: (File) input image (jpg/png)
45
+ - attribute: string (e.g., Smile, Age, Beard, Glasses, Makeup, Curly hair, Afro, Orange hair (text), Blonde hair (text))
46
+ - strength: float (see ranges from /api/attributes)
47
+ - align_face: boolean (true/false)
48
+ - use_bg_mask: boolean (true/false)
49
+ - custom_text_edit: string (optional, for text-based attributes)
50
+ - Curl (Windows path example):
51
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/image-edit" \
52
+ -H "Authorization: Bearer <ID_TOKEN>" \
53
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
54
+ -F "attribute=Smile" \
55
+ -F "strength=8" \
56
+ -F "align_face=true" \
57
+ -F "use_bg_mask=false" \
58
+ --output result_smile.png
59
+
60
+ Attribute shortcut endpoints (same body as above, omit attribute field)
61
+ - POST /api/smile
62
+ - POST /api/age
63
+ - POST /api/beard
64
+ - POST /api/mustache-goatee
65
+ - POST /api/glasses
66
+ - POST /api/makeup
67
+ - POST /api/curly-hair
68
+ - POST /api/afro
69
+ - POST /api/orange-hair-text
70
+ - POST /api/blonde-hair-text
71
+ - Curl (Age example):
72
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/age" \
73
+ -H "Authorization: Bearer <ID_TOKEN>" \
74
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
75
+ -F "strength=8" \
76
+ --output result_age.png
77
+
78
+ HTTP status codes
79
+ - 200: Success (image or JSON returned)
80
+ - 400: Bad request (missing/invalid params)
81
+ - 401: Unauthorized (missing/invalid/expired ID token)
82
+ - 404: Not found (wrong endpoint)
83
+ - 415: Unsupported media type (file field missing or not an image)
84
+ - 500: Internal server error
85
+
86
+ Troubleshooting
87
+ - 401 Invalid token: get a fresh idToken (tokens expire ~1h) and ensure you send it in the Authorization header, not in the URL.
88
+ - API key invalid when signing in: ensure you use the Web API key from the same Firebase project as the created user.
89
+ - Large images: processing can take longer; keep connection open until response.
90
+
91
+ Security notes
92
+ - Never commit service account JSON to the repo. Provide credentials via Space secret FIREBASE_CREDENTIALS_JSON or a secure env var.
93
+ - Prefer HTTPS for all client requests.
94
+
95
+ Web API key (how to find)
96
+ - Firebase Console → Project settings (gear) → General
97
+ - If you don’t have a Web app, click “Add app” → Web (</>) and finish
98
+ - Copy Web API key from:
99
+ - Project settings → General → Your project → Web API key, or
100
+ - Project settings → General → Your apps → select Web app → SDK setup and configuration → apiKey
101
+
102
+ Authentication cURL quick reference
103
+ - Sign up a user (optional; returns idToken too):
104
+ curl -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=YOUR_WEB_API_KEY" \
105
+ -H "Content-Type: application/json" \
106
+ -d '{"email":"you@example.com","password":"yourPassword","returnSecureToken":true}'
107
+
108
+ - Sign in (recommended; get idToken):
109
+ curl -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=YOUR_WEB_API_KEY" \
110
+ -H "Content-Type: application/json" \
111
+ -d '{"email":"you@example.com","password":"yourPassword","returnSecureToken":true}'
112
+ → Copy "idToken" and use:
113
+ curl -H "Authorization: Bearer <ID_TOKEN>" https://logicgoinfotechspaces-smile-changer.hf.space/api/me
114
+
115
+ Windows PowerShell line breaks
116
+ - Use backticks (`) instead of backslashes for multiline commands, or paste as a single line.
117
+
118
+ CURL CHEAT-SHEET (Firebase + all endpoints)
119
+
120
+ 1) Sign in (get idToken)
121
+ Replace YOUR_WEB_API_KEY and credentials; copy the idToken from response.
122
+ curl -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=YOUR_WEB_API_KEY" \
123
+ -H "Content-Type: application/json" \
124
+ -d '{"email":"you@example.com","password":"yourPassword","returnSecureToken":true}'
125
+
126
+ 2) Use YOUR_ID_TOKEN below; change file path as needed.
127
+
128
+ - Health (public)
129
+ curl https://logicgoinfotechspaces-smile-changer.hf.space/api/health
130
+
131
+ - Me (auth check)
132
+ curl -H "Authorization: Bearer YOUR_ID_TOKEN" \
133
+ https://logicgoinfotechspaces-smile-changer.hf.space/api/me
134
+
135
+ - List attributes
136
+ curl -H "Authorization: Bearer YOUR_ID_TOKEN" \
137
+ https://logicgoinfotechspaces-smile-changer.hf.space/api/attributes
138
+
139
+ - Generic image edit
140
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/image-edit" \
141
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
142
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
143
+ -F "attribute=Smile" \
144
+ -F "strength=8" \
145
+ -F "align_face=true" \
146
+ -F "use_bg_mask=false" \
147
+ --output result_generic.png
148
+
149
+ Shortcut endpoints (omit attribute field)
150
+
151
+ - Smile
152
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/smile" \
153
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
154
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
155
+ -F "strength=8" \
156
+ --output result_smile.png
157
+
158
+ - Age
159
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/age" \
160
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
161
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
162
+ -F "strength=8" \
163
+ --output result_age.png
164
+
165
+ - Beard (negative adds)
166
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/beard" \
167
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
168
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
169
+ -F "strength=-15" \
170
+ --output result_beard.png
171
+
172
+ - Mustache/Goatee (negative adds)
173
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/mustache-goatee" \
174
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
175
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
176
+ -F "strength=-5" \
177
+ --output result_goatee.png
178
+
179
+ - Glasses
180
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/glasses" \
181
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
182
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
183
+ -F "strength=20" \
184
+ --output result_glasses.png
185
+
186
+ - Makeup
187
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/makeup" \
188
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
189
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
190
+ -F "strength=5" \
191
+ --output result_makeup.png
192
+
193
+ - Curly hair
194
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/curly-hair" \
195
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
196
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
197
+ -F "strength=0.10" \
198
+ --output result_curly.png
199
+
200
+ - Afro
201
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/afro" \
202
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
203
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
204
+ -F "strength=0.12" \
205
+ --output result_afro.png
206
+
207
+ - Orange hair (text)
208
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/orange-hair-text" \
209
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
210
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
211
+ -F "strength=0.18" \
212
+ -F "custom_text_edit=styleclip_global_a face_a face with orange hair_0.18" \
213
+ --output result_orange_hair.png
214
+
215
+ - Blonde hair (text)
216
+ curl -X POST "https://logicgoinfotechspaces-smile-changer.hf.space/api/blonde-hair-text" \
217
+ -H "Authorization: Bearer YOUR_ID_TOKEN" \
218
+ -F "file=@C:\\projects\\smile_api\\selfi_4.jpeg" \
219
+ -F "strength=0.18" \
220
+ -F "custom_text_edit=styleclip_global_a face_a face with blonde hair_0.18" \
221
+ --output result_blonde_hair.png
app.py CHANGED
@@ -11,6 +11,48 @@ import io
11
  from spaces import GPU
12
  from huggingface_hub import snapshot_download
13
  from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  # Configure environment BEFORE importing any torch-dependent modules
16
  os.environ.setdefault("CUDA_VISIBLE_DEVICES", "")
@@ -250,12 +292,29 @@ api = FastAPI(title="Smile Changer API")
250
 
251
 
252
  def _require_auth(authorization: str | None = Header(default=None)):
 
 
 
253
  expected = os.getenv("API_AUTH_TOKEN", "logicgo_123")
254
  if not authorization or not authorization.startswith("Bearer "):
255
  raise HTTPException(status_code=401, detail="Missing or invalid Authorization header")
256
  token = authorization.split(" ", 1)[1]
257
- if token != expected:
258
- raise HTTPException(status_code=401, detail="Invalid token")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
 
261
  @api.get("/")
@@ -300,6 +359,19 @@ def ping(_: None = Depends(_require_auth)):
300
  return {"status": "ok", "auth": True}
301
 
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  @api.on_event("startup")
304
  def _self_check():
305
  try:
 
11
  from spaces import GPU
12
  from huggingface_hub import snapshot_download
13
  from PIL import Image
14
+ import json
15
+
16
+ try:
17
+ import firebase_admin
18
+ from firebase_admin import credentials, auth as fb_auth
19
+ except Exception: # firebase optional; enabled when installed
20
+ firebase_admin = None
21
+ credentials = None
22
+ fb_auth = None
23
+
24
+ FIREBASE_APP = None
25
+
26
+
27
+ def _init_firebase_if_possible() -> None:
28
+ global FIREBASE_APP
29
+ if FIREBASE_APP is not None:
30
+ return
31
+ if firebase_admin is None:
32
+ logger.info("firebase-admin not installed; skipping Firebase init")
33
+ return
34
+ # Service account via env var JSON or file path
35
+ sa_env = os.getenv("FIREBASE_CREDENTIALS_JSON", "").strip()
36
+ sa_path = "firebase_service_account.json"
37
+ try:
38
+ cred_obj = None
39
+ if sa_env:
40
+ # Allow raw JSON or file path
41
+ if os.path.exists(sa_env):
42
+ cred_obj = credentials.Certificate(sa_env)
43
+ else:
44
+ cred_obj = credentials.Certificate(json.loads(sa_env))
45
+ elif os.path.exists(sa_path):
46
+ cred_obj = credentials.Certificate(sa_path)
47
+ if cred_obj is not None:
48
+ FIREBASE_APP = firebase_admin.initialize_app(cred_obj)
49
+ logger.info("Firebase initialized successfully")
50
+ else:
51
+ logger.info("No Firebase credentials provided; skipping Firebase init")
52
+ except Exception as e:
53
+ logger.warning("Firebase init failed: %s", e)
54
+ FIREBASE_APP = None
55
+
56
 
57
  # Configure environment BEFORE importing any torch-dependent modules
58
  os.environ.setdefault("CUDA_VISIBLE_DEVICES", "")
 
292
 
293
 
294
  def _require_auth(authorization: str | None = Header(default=None)):
295
+ """Accepts either a static Bearer token (API_AUTH_TOKEN) or a Firebase ID token.
296
+ Returns a dict of auth info if authenticated; raises 401 otherwise.
297
+ """
298
  expected = os.getenv("API_AUTH_TOKEN", "logicgo_123")
299
  if not authorization or not authorization.startswith("Bearer "):
300
  raise HTTPException(status_code=401, detail="Missing or invalid Authorization header")
301
  token = authorization.split(" ", 1)[1]
302
+
303
+ # Static token fallback
304
+ if token == expected:
305
+ return {"auth": "static"}
306
+
307
+ # Firebase ID token verification (if configured)
308
+ _init_firebase_if_possible()
309
+ if firebase_admin is not None and fb_auth is not None and FIREBASE_APP is not None:
310
+ try:
311
+ claims = fb_auth.verify_id_token(token)
312
+ return {"auth": "firebase", "claims": claims, "uid": claims.get("uid")}
313
+ except Exception as e:
314
+ logger.warning("Firebase token verification failed: %s", e)
315
+
316
+ # If reached here, reject
317
+ raise HTTPException(status_code=401, detail="Invalid token")
318
 
319
 
320
  @api.get("/")
 
359
  return {"status": "ok", "auth": True}
360
 
361
 
362
+ @api.get("/api/me")
363
+ def me(user=Depends(_require_auth)):
364
+ # Returns auth mode and (if Firebase) user claims/uid
365
+ info = {"mode": user.get("auth")}
366
+ if user.get("auth") == "firebase":
367
+ info["uid"] = user.get("uid")
368
+ # Avoid returning all claims by default; include subset
369
+ claims = user.get("claims", {})
370
+ basic = {k: claims.get(k) for k in ("email", "name", "picture", "user_id", "uid") if claims.get(k) is not None}
371
+ info["claims"] = basic
372
+ return JSONResponse(info)
373
+
374
+
375
  @api.on_event("startup")
376
  def _self_check():
377
  try:
requirements.txt CHANGED
@@ -19,3 +19,4 @@ torchvision
19
  clip @ git+https://github.com/openai/CLIP.git@a1d071733d7111c9c014f024669f959182114e33
20
  spaces>=0.28.3
21
  dlib-binary
 
 
19
  clip @ git+https://github.com/openai/CLIP.git@a1d071733d7111c9c014f024669f959182114e33
20
  spaces>=0.28.3
21
  dlib-binary
22
+ firebase-admin>=6.5.0