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(