Address review feedback: Upgrade auth to RS256 signing, age-verification field, refresh-token sessions #33

Merged
reviewer-bot merged 7 commits from agent/424ce35e1b38 into main 2026-08-19 21:12:42 +00:00
2 changed files with 11 additions and 7 deletions
Showing only changes of commit 758307bbee - Show all commits
+2 -3
View File
@@ -26,9 +26,8 @@ class RefreshToken(Base):
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
# RS256 JWTs are ~420+ chars; Text avoids length issues and is fine for # RS256 JWTs are ~420+ chars; 512 is sufficient and matches the migration.
# a UUID-indexed lookup by hash in the future. token = Column(String(512), unique=True, index=True, nullable=False)
token = Column(String, unique=True, index=True, nullable=False)
expires_at = Column(DateTime(timezone=True), nullable=False) expires_at = Column(DateTime(timezone=True), nullable=False)
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now()) created_at = Column(DateTime(timezone=True), server_default=func.now())
+9 -4
View File
@@ -1,7 +1,7 @@
"""Authentication router - login, register, token refresh.""" """Authentication router - login, register, token refresh."""
import uuid import uuid
from datetime import datetime, timedelta, timezone from datetime import date, datetime, timedelta, timezone
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
@@ -76,8 +76,13 @@ def _create_access_token(data: dict) -> str:
def _create_refresh_token(data: dict) -> str: def _create_refresh_token(data: dict) -> str:
"""Create a signed refresh token using RS256.""" """Create a signed refresh token using RS256.
Includes a unique ``jti`` claim so that concurrent calls produce
distinct tokens even when issued within the same second.
"""
to_encode = data.copy() to_encode = data.copy()
to_encode["jti"] = str(uuid.uuid4())
expire = datetime.now(timezone.utc) + timedelta( expire = datetime.now(timezone.utc) + timedelta(
days=settings.REFRESH_TOKEN_EXPIRE_DAYS days=settings.REFRESH_TOKEN_EXPIRE_DAYS
) )
@@ -99,7 +104,7 @@ def hash_password(password: str) -> str:
return pwd_context.hash(password) return pwd_context.hash(password)
def calculate_age(birth_date, today=None) -> int: def calculate_age(birth_date: date, today: date | None = None) -> int:
"""Calculate age in years from a birth date. """Calculate age in years from a birth date.
Args: Args:
@@ -174,7 +179,7 @@ async def register(req: RegisterRequest, db: AsyncSession = Depends(get_db)):
date_of_birth=dob, date_of_birth=dob,
) )
db.add(user) db.add(user)
await db.commit() await db.flush() # flush to get user.id before creating refresh token
await db.refresh(user) await db.refresh(user)
# Create access token # Create access token