Skip to content

Commit 5cc9d67

Browse files
committed
fix: revent potential 500s: guard compare_digest() against non-str/bytes in typ & sub
Signed-off-by: Grant Ramsay <seapagan@gmail.com>
1 parent 02c8678 commit 5cc9d67

File tree

1 file changed

+25
-16
lines changed

1 file changed

+25
-16
lines changed

app/managers/auth.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,18 @@ async def refresh(
192192

193193
# Use constant-time comparison to prevent timing attacks
194194
token_type = payload.get("typ")
195-
if token_type is None or not secrets.compare_digest(
195+
if not isinstance(token_type, str) or not secrets.compare_digest(
196196
token_type, "refresh"
197197
):
198198
raise HTTPException(
199199
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
200200
)
201201

202202
user_id = payload.get("sub")
203-
if user_id is None:
203+
# Accept int-like strings but reject weird types early
204+
if isinstance(user_id, str) and user_id.isdigit():
205+
user_id = int(user_id)
206+
if not isinstance(user_id, int):
204207
raise HTTPException(
205208
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
206209
)
@@ -267,21 +270,24 @@ async def verify(code: str, session: AsyncSession) -> None:
267270
options={"verify_sub": False},
268271
)
269272

270-
user_id = payload.get("sub")
271-
if user_id is None:
272-
raise HTTPException(
273-
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
274-
)
275-
276273
# Use constant-time comparison to prevent timing attacks
277274
token_type = payload.get("typ")
278-
if token_type is None or not secrets.compare_digest(
275+
if not isinstance(token_type, str) or not secrets.compare_digest(
279276
token_type, "verify"
280277
):
281278
raise HTTPException(
282279
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
283280
)
284281

282+
user_id = payload.get("sub")
283+
# Accept int-like strings but reject weird types early
284+
if isinstance(user_id, str) and user_id.isdigit():
285+
user_id = int(user_id)
286+
if not isinstance(user_id, int):
287+
raise HTTPException(
288+
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
289+
)
290+
285291
user_data = await session.get(User, user_id)
286292

287293
if not user_data:
@@ -402,21 +408,24 @@ async def reset_password(
402408
options={"verify_sub": False},
403409
)
404410

405-
user_id = payload.get("sub")
406-
if user_id is None:
407-
raise HTTPException(
408-
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
409-
)
410-
411411
# Use constant-time comparison to prevent timing attacks
412412
token_type = payload.get("typ")
413-
if token_type is None or not secrets.compare_digest(
413+
if not isinstance(token_type, str) or not secrets.compare_digest(
414414
token_type, "reset"
415415
):
416416
raise HTTPException(
417417
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
418418
)
419419

420+
user_id = payload.get("sub")
421+
# Accept int-like strings but reject weird types early
422+
if isinstance(user_id, str) and user_id.isdigit():
423+
user_id = int(user_id)
424+
if not isinstance(user_id, int):
425+
raise HTTPException(
426+
status.HTTP_401_UNAUTHORIZED, ResponseMessages.INVALID_TOKEN
427+
)
428+
420429
user_data = await session.get(User, user_id)
421430

422431
if not user_data:

0 commit comments

Comments
 (0)