Spaces:
Running
Running
refactor: expand JWT algorithm support
Browse files- auth/routes.py +2 -20
- dependencies.py +16 -32
- main.py +1 -1
auth/routes.py
CHANGED
|
@@ -350,7 +350,7 @@ async def exchange_session(request: Request, response: Response):
|
|
| 350 |
payload = pyjwt.decode(
|
| 351 |
raw_supabase_token,
|
| 352 |
supabase_jwt_secret,
|
| 353 |
-
algorithms=["HS256"],
|
| 354 |
options={"verify_aud": False}, # Supabase uses 'authenticated' as aud
|
| 355 |
)
|
| 356 |
user_id = payload.get("sub")
|
|
@@ -358,26 +358,8 @@ async def exchange_session(request: Request, response: Response):
|
|
| 358 |
user_metadata = payload.get("user_metadata", {})
|
| 359 |
except pyjwt.ExpiredSignatureError:
|
| 360 |
raise HTTPException(401, "Supabase token has expired. Please sign in again.")
|
| 361 |
-
except pyjwt.InvalidTokenError as e:
|
| 362 |
-
raise HTTPException(401, f"Invalid Supabase token: {e}")
|
| 363 |
-
else:
|
| 364 |
-
# Fallback: decode claims without signature verification if secret is not set
|
| 365 |
-
try:
|
| 366 |
-
import time
|
| 367 |
-
payload = pyjwt.decode(
|
| 368 |
-
raw_supabase_token,
|
| 369 |
-
options={"verify_signature": False, "verify_aud": False},
|
| 370 |
-
)
|
| 371 |
-
exp = payload.get("exp")
|
| 372 |
-
if exp and time.time() > exp:
|
| 373 |
-
raise HTTPException(401, "Supabase token has expired. Please sign in again.")
|
| 374 |
-
user_id = payload.get("sub")
|
| 375 |
-
email = payload.get("email", "")
|
| 376 |
-
user_metadata = payload.get("user_metadata", {})
|
| 377 |
-
except HTTPException:
|
| 378 |
-
raise
|
| 379 |
except Exception:
|
| 380 |
-
pass
|
| 381 |
|
| 382 |
# Fallback: validate via Supabase API (slower, but works if unverified decoding failed)
|
| 383 |
if not user_id:
|
|
|
|
| 350 |
payload = pyjwt.decode(
|
| 351 |
raw_supabase_token,
|
| 352 |
supabase_jwt_secret,
|
| 353 |
+
algorithms=["HS256", "HS384", "HS512"],
|
| 354 |
options={"verify_aud": False}, # Supabase uses 'authenticated' as aud
|
| 355 |
)
|
| 356 |
user_id = payload.get("sub")
|
|
|
|
| 358 |
user_metadata = payload.get("user_metadata", {})
|
| 359 |
except pyjwt.ExpiredSignatureError:
|
| 360 |
raise HTTPException(401, "Supabase token has expired. Please sign in again.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
except Exception:
|
| 362 |
+
pass # Fall through to Supabase API validation below
|
| 363 |
|
| 364 |
# Fallback: validate via Supabase API (slower, but works if unverified decoding failed)
|
| 365 |
if not user_id:
|
dependencies.py
CHANGED
|
@@ -88,27 +88,19 @@ async def get_current_user_id(request: Request) -> str:
|
|
| 88 |
pass
|
| 89 |
|
| 90 |
# ββ Mode 1b: Supabase JWT stateless fallback (avoids 403 network race) βββ
|
| 91 |
-
|
| 92 |
-
|
| 93 |
payload = pyjwt.decode(
|
| 94 |
token,
|
| 95 |
settings.supabase_jwt_secret,
|
| 96 |
-
algorithms=["HS256"],
|
| 97 |
options={"verify_aud": False},
|
| 98 |
)
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
exp = payload.get("exp")
|
| 105 |
-
if exp and time.time() > exp:
|
| 106 |
-
payload = {}
|
| 107 |
-
user_id = payload.get("sub")
|
| 108 |
-
if user_id:
|
| 109 |
-
return str(user_id)
|
| 110 |
-
except Exception:
|
| 111 |
-
pass
|
| 112 |
|
| 113 |
# ββ Mode 2: Supabase token (backwards-compat for Google OAuth sessions) ββ
|
| 114 |
try:
|
|
@@ -142,27 +134,19 @@ async def get_current_user(request: Request) -> Any:
|
|
| 142 |
pass
|
| 143 |
|
| 144 |
# ββ Mode 1b: Supabase JWT stateless fallback βββββββββββββββββββββββββββββ
|
| 145 |
-
|
| 146 |
-
|
| 147 |
payload = pyjwt.decode(
|
| 148 |
token,
|
| 149 |
settings.supabase_jwt_secret,
|
| 150 |
-
algorithms=["HS256"],
|
| 151 |
options={"verify_aud": False},
|
| 152 |
)
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
exp = payload.get("exp")
|
| 159 |
-
if exp and time.time() > exp:
|
| 160 |
-
payload = {}
|
| 161 |
-
user_id = payload.get("sub")
|
| 162 |
-
if user_id:
|
| 163 |
-
return {"id": user_id, "email": payload.get("email", "")}
|
| 164 |
-
except Exception:
|
| 165 |
-
pass
|
| 166 |
|
| 167 |
# ββ Mode 2: Supabase token (backwards compat) ββββββββββββββββββββββββββββ
|
| 168 |
try:
|
|
|
|
| 88 |
pass
|
| 89 |
|
| 90 |
# ββ Mode 1b: Supabase JWT stateless fallback (avoids 403 network race) βββ
|
| 91 |
+
if settings.supabase_jwt_secret:
|
| 92 |
+
try:
|
| 93 |
payload = pyjwt.decode(
|
| 94 |
token,
|
| 95 |
settings.supabase_jwt_secret,
|
| 96 |
+
algorithms=["HS256", "HS384", "HS512"],
|
| 97 |
options={"verify_aud": False},
|
| 98 |
)
|
| 99 |
+
user_id = payload.get("sub")
|
| 100 |
+
if user_id:
|
| 101 |
+
return str(user_id)
|
| 102 |
+
except Exception:
|
| 103 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
# ββ Mode 2: Supabase token (backwards-compat for Google OAuth sessions) ββ
|
| 106 |
try:
|
|
|
|
| 134 |
pass
|
| 135 |
|
| 136 |
# ββ Mode 1b: Supabase JWT stateless fallback βββββββββββββββββββββββββββββ
|
| 137 |
+
if settings.supabase_jwt_secret:
|
| 138 |
+
try:
|
| 139 |
payload = pyjwt.decode(
|
| 140 |
token,
|
| 141 |
settings.supabase_jwt_secret,
|
| 142 |
+
algorithms=["HS256", "HS384", "HS512"],
|
| 143 |
options={"verify_aud": False},
|
| 144 |
)
|
| 145 |
+
user_id = payload.get("sub")
|
| 146 |
+
if user_id:
|
| 147 |
+
return {"id": str(user_id), "email": payload.get("email", "")}
|
| 148 |
+
except Exception:
|
| 149 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
# ββ Mode 2: Supabase token (backwards compat) ββββββββββββββββββββββββββββ
|
| 152 |
try:
|
main.py
CHANGED
|
@@ -95,7 +95,7 @@ app = FastAPI(
|
|
| 95 |
title="AI Tutor API",
|
| 96 |
description="Backend API for the AI Tutor for Students application",
|
| 97 |
version="1.0.0",
|
| 98 |
-
lifespan=lifespan,
|
| 99 |
)
|
| 100 |
|
| 101 |
@app.middleware("http")
|
|
|
|
| 95 |
title="AI Tutor API",
|
| 96 |
description="Backend API for the AI Tutor for Students application",
|
| 97 |
version="1.0.0",
|
| 98 |
+
# lifespan=lifespan,
|
| 99 |
)
|
| 100 |
|
| 101 |
@app.middleware("http")
|