📄 HANDOFF-SOCRATES-v3.md 4,226 bytes May 02, 2026 📋 Raw

Handoff: RTSport Auth Layer (JWT + Role Claims)

What: Implement JWT authentication and role-based access control. Replace stub X-Role headers with real JWT tokens containing role claims.

Why: Integration testing is complete. The backend works with stub roles. Now we need real auth before production deployment.

Files:
- /home/hoffmann_admin/.openclaw/shared/build-20260501/backend/app/api/ — All endpoint files (roster.py, cases.py, events.py)
- /home/hoffmann_admin/.openclaw/shared/build-20260501/backend/app/config.py — Settings (secret_key, token expiry)
- /home/hoffmann_admin/.openclaw/shared/build-20260501/backend/app/database.py — Session management
- /home/hoffmann_admin/.openclaw/shared/build-20260501/backend/app/models.py — Need User model

Current State:
- Stubs: X-Role: at|coach|parent and X-User-ID: usr_001 headers
- FERPA filtering works with stubs
- Integration tests passed with stub roles

What You Need to Implement:

1. User Model

class User(Base):
    __tablename__ = "users"

    id = Column(String, primary_key=True)
    school_id = Column(String, ForeignKey("schools.id"), nullable=False)
    email = Column(String, unique=True, nullable=False)
    hashed_password = Column(String, nullable=False)
    role = Column(String, nullable=False)  # "at" | "coach" | "parent" | "admin"
    first_name = Column(String)
    last_name = Column(String)
    is_active = Column(Boolean, default=True)
    created_at = Column(DateTime, default=datetime.utcnow)

2. Auth Dependencies (FastAPI)

app/auth.py:
- create_access_token(user_id, role, school_id) → JWT string
- verify_token(token) → dict with user_id, role, school_id
- get_current_user(token: str = Header(...)) → User model
- require_role(roles: List[str]) → dependency factory

JWT Payload:

{
  "sub": "usr_001",
  "role": "at",
  "school_id": "schl_001",
  "exp": 1719878400
}

3. Update Endpoints

Replace all stub role checks with real auth:

# BEFORE (stub):
role = request.headers.get("X-Role", "at")
user_id = request.headers.get("X-User-ID", "usr_001")

# AFTER (JWT):
from app.auth import get_current_user, require_role

current_user: User = Depends(get_current_user)

# Role-protected endpoint:
@router.get("/roster")
async def get_roster(
    current_user: User = Depends(require_role(["at", "coach"])),
    db: Session = Depends(get_db)
):
    # current_user.role, current_user.school_id available
    ...

4. Password Hashing

Use passlib with bcrypt:
- hash_password(password) → hashed string
- verify_password(plain, hashed) → bool

5. Login Endpoint

POST /api/v1/auth/login

// Request
{
  "email": "at@preble.k12.wi.us",
  "password": "..."
}

// Response
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "role": "at",
  "school_id": "schl_001"
}

6. Parent Linking

Athlete's parent_ids array links to User IDs. When a parent logs in:
- JWT contains their user_id
- Cases endpoint filters: athlete.parent_ids.contains(current_user.id)

7. Update Integration Tests

Replace stub headers with JWT tokens in test scripts.

Scope:
- ✅ DO: Create User model
- ✅ DO: Implement JWT auth
- ✅ DO: Update all endpoints with real auth dependencies
- ✅ DO: Add login endpoint
- ✅ DO: Update integration tests
- ❌ DON'T: Change schemas or business logic
- ❌ DON'T: Write frontend auth UI (Daedalus handles that)
- ❌ DON'T: Add refresh tokens (out of scope)

Success Criteria:
- All endpoints require valid JWT
- Role-based access enforced (AT full, Coach team-scoped, Parent own-child)
- FERPA filtering uses real user role from JWT
- Integration tests pass with JWT tokens
- Passwords are hashed (never stored plain)

ETA: TBD — provide your own after review

Dependencies to Add:

python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.9  (already in requirements.txt)

Coordination:
- Frontend will need to store JWT and send as Authorization: Bearer <token> header
- Daedalus should be notified when auth is ready
- Update integration.md with auth setup instructions