Skip to content

Commit 0d17e75

Browse files
committed
fix(oauth): strengthen state parameter validation
- Validate state format is exactly 'user_id:token' - Check user_id matches authenticated user - Verify token length (43 chars from token_urlsafe(32)) - Validate token charset (URL-safe base64: A-Za-z0-9_-) Replaces weak prefix-only check with full format validation.
1 parent 1559f1d commit 0d17e75

1 file changed

Lines changed: 18 additions & 3 deletions

File tree

backend/routes/github.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Frontend never sees the GitHub access token.
77
"""
88
import os
9+
import re
910
import secrets
1011
import httpx
1112
from fastapi import APIRouter, HTTPException, Depends, Query
@@ -205,9 +206,23 @@ async def github_oauth_callback(
205206
if not GITHUB_CLIENT_ID or not GITHUB_CLIENT_SECRET:
206207
raise HTTPException(status_code=500, detail="GitHub OAuth not configured")
207208

208-
# Verify state contains user_id (basic CSRF protection)
209-
if not request.state.startswith(auth.user_id):
210-
raise HTTPException(status_code=400, detail="Invalid state parameter")
209+
# Verify state format and user_id match
210+
# State format: user_id:random_token (where random_token is base64url from token_urlsafe)
211+
state_parts = request.state.split(":", 1)
212+
if len(state_parts) != 2:
213+
raise HTTPException(status_code=400, detail="Invalid state format")
214+
215+
state_user_id, state_token = state_parts
216+
if state_user_id != auth.user_id:
217+
raise HTTPException(status_code=400, detail="State user mismatch")
218+
219+
# Validate token portion: token_urlsafe(32) produces 43 chars of URL-safe base64
220+
if len(state_token) != 43:
221+
raise HTTPException(status_code=400, detail="Invalid state token length")
222+
223+
# Validate charset (URL-safe base64: A-Z, a-z, 0-9, -, _)
224+
if not re.match(r'^[A-Za-z0-9_-]+$', state_token):
225+
raise HTTPException(status_code=400, detail="Invalid state token charset")
211226

212227
# Exchange code for access token
213228
async with httpx.AsyncClient() as client:

0 commit comments

Comments
 (0)