HTTP 403 Forbidden means the server understood a request but refuses it because the requester lacks sufficient permission. A validly authenticated user may receive 403 when accessing another user’s registration or an administrator-only endpoint. This differs from 401 Unauthorized, which indicates missing or invalid authentication credentials. Framework behavior can blur the distinction: an identified FastAPI bug caused some unauthenticated requests to return 403 instead of 401. section 12.4 section 4.8
Use 403 as an authorization decision, then verify that the decision matches the request context:
- For a protected resource, validate the access token and confirm that its claims identify a permitted user. section 4.8
- For role-restricted functionality, check the permissions claim for the required role, such as admin, and return 403 when it is absent. section 4.8
- Test unauthorized access to course lessons, another student’s registration, and administrative routes; assert the expected response code. section 12.4 section 4.8
Authorization test
A test creates a token for one student, requests another student’s registration, and expects 403. A separate role-based test requests /admin/students with a non-admin token and also expects 403. These checks expose broken object-level or function-level authorization. section 12.4 section 12.4
Listing 12.2 Running a broken authentication test
# file: ch12/tests.py
from sqlalchemy import select
from starlette_testclient import TestClient
from db_session import session_maker
from models import Course
from server import server
client = TestClient(server) #1
def test_broken_authentication():
with session_maker() as session: #2
course = session.scalar(select(Course)) #3
lessons = course.lessons
response = client.get(
f"/courses/{course.id}/lessons/{lessons[0].id}"
) #4
assert response.status_code == 403 #5
Inspect the complete pytest example for creating test data, calling a lesson endpoint without authentication, and asserting its response status. section 12.4
