Protect List and Get

Add authentication and user filtering to the list and get endpoints

💻

Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.

Two endpoints, two different patterns

The list_expenses and get_expense endpoints both need authentication, but they filter differently.

list_expenses returns a collection. You add a where clause to the query so each user only sees their own expenses. Without this filter, a logged-in user could see every expense in the database.

get_expense returns a single expense by its identifier. The database lookup still uses session.get(), but you add an ownership check afterward. If the expense belongs to a different user, the endpoint returns 404 — the same response as "expense not found." Returning 404 instead of 403 is intentional. A 403 response would confirm that the expense exists but belongs to someone else. A 404 reveals nothing.

Instructions

Protect the list_expenses and get_expense endpoints.

  1. Add current_user: CurrentUser as a parameter to list_expenses, after session: SessionDep — this requires a valid token before the endpoint runs.
  2. Right after statement = select(Expense), add a filter so each user only sees their own data: statement = statement.where(Expense.user_id == current_user.id). Without this, any logged-in user would see everyone's expenses.
  3. Add current_user: CurrentUser as a parameter to get_expense, after session: SessionDep.
  4. After the existing 404 check (the if not expense block), add an ownership check: if expense.user_id != current_user.id, raise HTTPException(status_code=404, detail="Expense not found"). You return 404, not 403, so an attacker cannot tell whether the expense exists at all.