rpmjp/portfolio
rpmjp/projects/skillbridge/auth_deps.py
CompletedFebruary – April 2026

SkillBridge AI — Test Prep Academy Platform

Multi-tenant AI learning platform for test prep academies. Socratic AI tutor that refuses to give answers, AI quiz generation from uploaded files, async pre-grading, weak-spot detection, and AI parent summaries. Full role-based platform across admin, instructor, student, and parent.

LIVE DEMO
Python 3.12FastAPIPostgreSQL 16SQLAlchemy 2.0ReactTypeScriptTailwindGroq / Llama
Languages
Python54.2%
TypeScript45.1%
CSS0.3%
JavaScript0.2%
Dockerfile0.1%
Mako0.1%
auth_deps.py
"""FastAPI auth dependencies.

Every protected endpoint depends on get_current_user (decode JWT, load user).
require_role is a dependency factory for role-gated endpoints — the role check
runs before any handler code, so there's no path to a protected endpoint that
skips it. This is the enforcement point for the role-filtering half of the
multi-tenancy model.
"""

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session

from app.db.session import get_db
from app.models.user import User, UserRole
from app.services.auth_service import get_user_from_token


# tokenUrl points to the form-based login endpoint for the Swagger Authorize button.
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login/form")


def get_current_user(
    token: str = Depends(oauth2_scheme),
    db: Session = Depends(get_db),
) -> User:
    """Decode the JWT and load the user. The user carries the tenant_id that
    scopes every downstream query — tenant is derived from identity here,
    never from a client-supplied request parameter."""
    return get_user_from_token(db, token)


def require_role(*allowed_roles: UserRole):
    """Dependency factory for role-gated endpoints.

    Usage:
        @router.post("/quizzes/generate")
        def generate(user: User = Depends(require_role(UserRole.INSTRUCTOR, UserRole.ADMIN))):
            ...

    The returned dependency runs before the handler. A student hitting an
    instructor-only endpoint gets a 403 before any handler logic executes.
    """
    def _checker(current_user: User = Depends(get_current_user)) -> User:
        if current_user.role not in allowed_roles:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Insufficient permissions",
            )
        return current_user

    return _checker