Python FastAPI Tutorial (Part 11): Authorization - Protecting Routes and Verifying Current User
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Create a reusable get current user dependency that validates JWTs and loads the authenticated user from the database, raising 401 Unauthorized on any failure.
Briefing
Authorization becomes real only after the API stops trusting client-supplied user IDs. This tutorial turns a working JWT login system into route protection by introducing a reusable “current user” dependency that validates the token, loads the user from the database, and injects the authenticated user into any endpoint that needs it. The payoff is immediate: post creation no longer lets clients impersonate other users, and edit/delete operations gain strict ownership rules.
The starting point is a security gap. The post creation schema still accepts a user_id from the request body, and the front end uses hard-coded user IDs. That means anyone can create, edit, or delete posts for any account simply by changing IDs. The fix is to remove user_id from the client payload and instead derive it from the trusted JWT. A new dependency is built by combining existing pieces: an OAuth2 token extractor that reads the Authorization header and a verify_access_token function that checks validity and returns a user identifier. The dependency then converts the token’s user ID to an integer, queries the database for the corresponding user, and raises a 401 Unauthorized when the token is missing/invalid/expired or when the user no longer exists.
To make this ergonomic, the tutorial defines a type alias (current user) using FastAPI’s annotated dependency pattern. Routes can then declare current user as a parameter, and FastAPI automatically enforces authentication before the endpoint logic runs. With that in place, the postcreate schema drops the user_id field entirely, and the post creation endpoint assigns post.user_id from current_user.id. The code also shrinks because the manual “verify user exists” logic is no longer needed.
Ownership checks tighten the remaining holes. For update and delete endpoints, the tutorial adds the current user dependency and then compares the post’s stored owner ID to current_user.id. If they don’t match, the API returns 403 Forbidden with a clear “not authorized” message—distinguishing “not authenticated” (401) from “authenticated but not permitted” (403). The same pattern is applied to user profile update and user deletion, ensuring users can only modify or remove their own accounts.
A major usability upgrade follows on the front end. API calls now include the Authorization: Bearer <token> header, and client-side handlers redirect to login on 401. Edit/delete buttons on post pages are hidden unless the logged-in user owns the post, with JavaScript performing the ownership comparison after fetching the current user. The tutorial also adds an account page that fetches the current user via the /me endpoint and supports profile updates and account deletion. Importantly, the account page itself isn’t server-protected because the server can’t read localStorage tokens during template rendering; real security still lives in the API, where missing/invalid tokens trigger 401 and ownership mismatches trigger 403.
End-to-end testing confirms the behavior: unauthenticated requests to protected routes fail, posts created by one user can’t be edited or deleted by another, and deleting an account removes that user’s posts. The result is a production-style authorization layer built directly on top of JWT authentication—no more hard-coded IDs, and no more client-controlled identity.
Cornell Notes
The tutorial upgrades a JWT login system into real authorization by adding a reusable FastAPI dependency that returns the authenticated user. That dependency extracts the token from the Authorization header, verifies it, converts the token user ID, queries the database for the user, and raises 401 Unauthorized on any failure. Routes then declare a current user parameter so authentication happens automatically before endpoint code runs. Post creation stops accepting user_id from the request body and instead assigns ownership from the token-derived user. Update and delete endpoints add ownership checks that return 403 Forbidden when the authenticated user doesn’t own the target resource. The front end is updated to send Authorization headers and to hide edit/delete controls unless ownership matches.
Why is accepting user_id in the post creation request body a security problem, and how does the tutorial fix it?
What does the reusable get current user dependency do, step by step, and what errors does it raise?
How does the tutorial distinguish between authentication failures and authorization failures?
How do ownership checks work for editing and deleting posts?
Why isn’t the account page protected on the server the same way as API routes?
What front-end changes are required for the protected API to work?
Review Questions
- What specific fields are removed from the post creation request, and where does the server get the post owner ID instead?
- Why does the tutorial use 403 Forbidden for ownership mismatches rather than 401 Unauthorized?
- How does the /me endpoint relate to the current user dependency and the front-end account page behavior?
Key Points
- 1
Create a reusable get current user dependency that validates JWTs and loads the authenticated user from the database, raising 401 Unauthorized on any failure.
- 2
Remove user_id from the post creation request body so clients can’t impersonate other users; assign post.user_id from current_user.id.
- 3
Protect endpoints by adding the current user dependency parameter to route signatures so authentication happens before endpoint logic runs.
- 4
Implement ownership checks for update and delete: compare post.user_id to current_user.id and return 403 Forbidden when they don’t match.
- 5
Update front-end API calls to send Authorization: Bearer <token> and handle 401 by redirecting to login.
- 6
Hide edit/delete UI controls on the front end unless the logged-in user owns the post, but rely on the backend for real security.
- 7
Use client-side guarding for the account page (since tokens live in localStorage) while keeping backend authorization as the enforcement layer.