fix(auth): ensure JWT sub claim is RFC 7519 compliant string

- Change TokenPayload.sub type from int to str per JWT RFC 7519
- Add user_id property to TokenPayload for int conversion
- Update token creation to serialize user_id as string
- Update token consumers to use payload.user_id
- Change dashboard port from 3000 to 8080
- Add pydantic[email] for email validation
- Update default admin email to admin@sparc.dev

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-03-14 14:22:30 -04:00
parent 258b349e98
commit ebba983a1d
5 changed files with 13 additions and 7 deletions
+1 -1
View File
@@ -236,7 +236,7 @@ async def refresh_token(request: RefreshRequest):
)
db = get_db_client()
user = db.get_user_by_id(payload.sub)
user = db.get_user_by_id(payload.user_id)
if not user:
raise HTTPException(
+9 -4
View File
@@ -24,12 +24,17 @@ security = HTTPBearer()
class TokenPayload(BaseModel):
"""JWT token payload."""
sub: int # user_id
sub: str # user_id as string (JWT RFC 7519 requires sub to be a string)
email: str
role: str
exp: datetime
type: str # "access" or "refresh"
@property
def user_id(self) -> int:
"""Get user_id as integer."""
return int(self.sub)
class TokenResponse(BaseModel):
"""Token response model."""
@@ -61,7 +66,7 @@ def create_access_token(user_id: int, email: str, role: str) -> str:
"""
expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
payload = {
"sub": user_id,
"sub": str(user_id),
"email": email,
"role": role,
"exp": expire,
@@ -83,7 +88,7 @@ def create_refresh_token(user_id: int, email: str, role: str) -> str:
"""
expire = datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
payload = {
"sub": user_id,
"sub": str(user_id),
"email": email,
"role": role,
"exp": expire,
@@ -166,7 +171,7 @@ async def get_current_user(
)
db = get_db_client()
user = db.get_user_by_id(payload.sub)
user = db.get_user_by_id(payload.user_id)
if not user:
raise HTTPException(
+1 -1
View File
@@ -53,7 +53,7 @@ services:
build: ./frontend
container_name: sparc-dashboard
ports:
- "3000:80"
- "8080:80"
depends_on:
- api
restart: unless-stopped
+1
View File
@@ -8,6 +8,7 @@ openai
psycopg2-binary
fastapi
uvicorn[standard]
pydantic[email]
httpx
numpy
pandas
+1 -1
View File
@@ -19,7 +19,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from SPARC import config
from SPARC.database import DatabaseClient
DEFAULT_ADMIN_EMAIL = "admin@sparc.local"
DEFAULT_ADMIN_EMAIL = "admin@sparc.dev"
def generate_password(length: int = 16) -> str: