Registration: replace date-of-birth field with a simple 18+ confirmation #45

Merged
reviewer-bot merged 4 commits from agent/f4c371c981ba into main 2026-08-20 16:51:43 +00:00
Showing only changes of commit 4ec71b87b9 - Show all commits
+7 -41
View File
@@ -1,13 +1,13 @@
"""Authentication router - login, register, token refresh."""
import uuid
from datetime import date, datetime, timedelta, timezone
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel, EmailStr, field_validator
from pydantic import BaseModel, EmailStr
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -39,17 +39,7 @@ class RegisterRequest(BaseModel):
username: str
email: EmailStr
password: str
date_of_birth: str # ISO format date string (YYYY-MM-DD)
@field_validator("date_of_birth")
@classmethod
def validate_date_of_birth(cls, v: str) -> str:
"""Validate that the date string is a valid date in YYYY-MM-DD format."""
try:
datetime.strptime(v, "%Y-%m-%d")
except ValueError:
raise ValueError("date_of_birth must be in YYYY-MM-DD format")
return v
over_18: bool # Simple boolean confirmation instead of date_of_birth
class UserResponse(BaseModel):
@@ -115,26 +105,6 @@ def hash_password(password: str) -> str:
return pwd_context.hash(password)
def calculate_age(birth_date: date, today: date | None = None) -> int:
"""Calculate age in years from a birth date.
Args:
birth_date: The person's date of birth.
today: The reference date. Defaults to today's date (UTC).
Returns:
Age in whole years as of the reference date.
"""
if today is None:
today = datetime.now(timezone.utc).date()
age = today.year - birth_date.year
# Adjust if birthday hasn't occurred yet this year
if (today.month, today.day) < (birth_date.month, birth_date.day):
age -= 1
return age
def _issue_refresh_token(user_id: int, db: AsyncSession) -> str:
"""Create and persist a refresh token for the given user."""
refresh_token_value = _create_refresh_token({"sub": str(user_id)})
@@ -155,16 +125,12 @@ def _issue_refresh_token(user_id: int, db: AsyncSession) -> str:
async def register(req: RegisterRequest, db: AsyncSession = Depends(get_db)):
"""Register a new user and return access + refresh tokens.
Requires date_of_birth and rejects users under 18.
Requires over_18 boolean confirmation; rejects users who are not 18+.
"""
# Parse and validate date_of_birth
dob = datetime.strptime(req.date_of_birth, "%Y-%m-%d").date()
age = calculate_age(dob)
if age < 18:
if not req.over_18:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Must be 18 or older to register (provided date of birth: {req.date_of_birth})",
detail="Must be 18 or older to register",
)
# Check if username already exists
@@ -187,7 +153,7 @@ async def register(req: RegisterRequest, db: AsyncSession = Depends(get_db)):
username=req.username,
email=req.email,
hashed_password=hash_password(req.password),
date_of_birth=dob,
date_of_birth=None, # No longer collecting DOB
)
db.add(user)
await db.flush() # flush to get user.id before creating refresh token