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.""" """Authentication router - login, register, token refresh."""
import uuid import uuid
from datetime import date, datetime, timedelta, timezone from datetime import 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
from jose import JWTError, jwt from jose import JWTError, jwt
from passlib.context import CryptContext from passlib.context import CryptContext
from pydantic import BaseModel, EmailStr, field_validator from pydantic import BaseModel, EmailStr
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -39,17 +39,7 @@ class RegisterRequest(BaseModel):
username: str username: str
email: EmailStr email: EmailStr
password: str password: str
date_of_birth: str # ISO format date string (YYYY-MM-DD) over_18: bool # Simple boolean confirmation instead of date_of_birth
@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
class UserResponse(BaseModel): class UserResponse(BaseModel):
@@ -115,26 +105,6 @@ def hash_password(password: str) -> str:
return pwd_context.hash(password) 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: def _issue_refresh_token(user_id: int, db: AsyncSession) -> str:
"""Create and persist a refresh token for the given user.""" """Create and persist a refresh token for the given user."""
refresh_token_value = _create_refresh_token({"sub": str(user_id)}) 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)): async def register(req: RegisterRequest, db: AsyncSession = Depends(get_db)):
"""Register a new user and return access + refresh tokens. """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 if not req.over_18:
dob = datetime.strptime(req.date_of_birth, "%Y-%m-%d").date()
age = calculate_age(dob)
if age < 18:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Check if username already exists
@@ -187,7 +153,7 @@ async def register(req: RegisterRequest, db: AsyncSession = Depends(get_db)):
username=req.username, username=req.username,
email=req.email, email=req.email,
hashed_password=hash_password(req.password), hashed_password=hash_password(req.password),
date_of_birth=dob, date_of_birth=None, # No longer collecting DOB
) )
db.add(user) db.add(user)
await db.flush() # flush to get user.id before creating refresh token await db.flush() # flush to get user.id before creating refresh token