Python FastAPI Tutorial (Part 11): Authorization - Protecting Routes and Verifying Current User
38:24

Python FastAPI Tutorial (Part 11): Authorization - Protecting Routes and Verifying Current User

Corey Schafer 04.02.2026 7 445 просмотров 263 лайков

Machine-readable: Markdown · JSON API · Site index

Поделиться Telegram VK Бот
Транскрипт Скачать .md
Анализ с AI
Описание видео
In this video, we'll be learning how to protect our FastAPI routes with proper authorization. We'll build a reusable get_current_user dependency that validates tokens and returns the authenticated user, remove the hardcoded user_id from our schemas and frontend, add ownership checks so users can only edit and delete their own content, and build an Account page for profile management. By the end of this tutorial, our application will have a complete authorization layer on top of the authentication system we built in the previous video. Let's get started... The code from this video can be found here: https://github.com/CoreyMSchafer/FastAPI-11-Authorization Full FastAPI Course: https://www.youtube.com/playlist?list=PL-osiE80TeTsak-c-QsVeg0YYG_0TeyXI ✅ Support My Channel Through Patreon: https://www.patreon.com/coreyms ✅ Become a Channel Member: https://www.youtube.com/channel/UCCezIgC97PvUuR4_gbFUs5g/join ✅ One-Time Contribution Through PayPal: https://goo.gl/649HFY ✅ Cryptocurrency Donations: Bitcoin Wallet - 3MPH8oY2EAgbLVy7RBMinwcBntggi7qeG3 Ethereum Wallet - 0x151649418616068fB46C3598083817101d3bCD33 Litecoin Wallet - MPvEBY5fxGkmPQgocfJbxP6EmTo5UUXMot ✅ Corey's Public Amazon Wishlist http://a.co/inIyro1 ✅ Equipment I Use and Books I Recommend: https://www.amazon.com/shop/coreyschafer ▶️ You Can Find Me On: My Website - http://coreyms.com/ My Second Channel - https://www.youtube.com/c/coreymschafer Facebook - https://www.facebook.com/CoreyMSchafer Twitter - https://twitter.com/CoreyMSchafer Instagram - https://www.instagram.com/coreymschafer/ #Python #FastAPI

Оглавление (8 сегментов)

Segment 1 (00:00 - 05:00)

Hey there. How's it going everybody? In this video, we're going to be learning how to protect our routes with proper authorization. Now, this is where all the pieces that we built in the last video finally come together. So, let's do a quick recap of where we currently are. So, in the last tutorial, we built a complete registration and login system. We have JSON web tokens being generated and stored in local storage. We added some JavaScript to the front end to manage authentication state. And we built that forward slashme endpoint that validates tokens and returns the current user if one is logged in. But here's the thing, we're not actually using this authentication yet. If you look at our front-end code right now, we still have hard-coded user IDs equal to one everywhere. And anyone can still create, edit, or delete any post because we're not checking who's making the request. So all the work that we did setting up authentication isn't actually protecting anything yet. And some of this carries over into the API as well because our postcreate schema takes in a user ID. If I look here, we can see that this takes in a user ID when creating a post that can be assigned to any user. But we want to limit post creation to the current loggedin user. So that's what we're going to fix in this tutorial. So this is the big payoff where we actually use authentication to protect our routes. We will create a reusable git current user dependency. We will remove this user ID from our postcreate schema. Uh we'll replace all of those hard-coded values on the front end with real uh authentication. We'll add ownership checks so users can only edit or delete their own content. And we will also build an account page for profile management. Okay, so let's go ahead and get started. So if I open my code here, I'm going to open our schemas. p py file here. And now let's look at our current schemas. So if I go down to this postcreate schema here, we can see that our postcreate schema has a user ID field that comes from the request body. So this means that anyone can claim to be any user just by sending a different user ID. So obviously that's not good. Uh and we can see that we marked this as temporary when we put it in place. So we only want the authenticated user to be able to create or post by themselves. So the solution is to get the user ID from the token instead of the request body. The token was issued by our server so we can trust it. Uh we know who created that token and when. So let's start fixing this by creating a reusable dependency. And to do that I will open up our o. py file here and we will put that within here. Now, we already have a lot of good stuff that we put in here in the last video. So, we already have verify access token that returns the user ID if the token is valid. And we have the OOTH 2 scheme that extracts the token from the authorization header. Now, we need to combine these into a dependency that looks up the user from the database and returns the full user object. So, this is different from the for/me endpoint that we built last time. The for/me endpoint is an API endpoint that users call directly. What we're building now is a dependency that other routes use internally to require authentication. So first let me add the imports that we need at the top. So first we're going to need annotated from typing uh for our dependency typent. So from typing we want to import uh annotated and from fast API we're going to need depends and http exception and also status and also we're going to be making a database query here. So we need a couple of SQL alchemy imports as well. So, I'm going to import select from SQL Alchemy and from SQL Alchemy. Whoops. Let me uh spell that correctly there. So, from SQL Alchemy. ext. ync io, we want to import async session. And finally, we need to import our database models. And also from our database file, we also need to import git db. Now this autosorts my imports whenever I save, but you can see the ones that are currently unused are underlined here. So that's everything that we imported there. Now at the bottom of the file after verify access token, uh let's add our dependency. Now, this is a bit of a long function. So, I'm going to grab this from our snippets. But I'll explain this completely after we add it. So, in my snippets here, let me grab this get current user dependency here. And let's put this in. Okay. So, let me walk

Segment 2 (05:00 - 10:00)

through what this does. So, it takes a token and this is going to be from the authorization header using the OOTH 2 scheme here. and it takes a database session. Then it verifies the token using our verify access token function from the last video. If that returns none, meaning that the token is invalid or expired, then we raise a 401 unauthorized uh return there, then we convert the user ID to an integer because it's stored as a string in the token. If that conversion fails, then we also raise a 401. And then finally we query the database to get the actual user object. And if the uh user doesn't exist like if they were deleted then again we raise a 401. But if everything checks out then we return the user object. Now any route that uses this dependency will automatically require a valid token and get access to the full user object. So now let's make this even easier to use here. So, right below this function, I'm going to add a type alias for this. And the type alias, I will call this current user. Let's set this equal to annotated. And within here, we'll say models. user. And then this user depends on get current user, which is the function that we just created here. Now, if you don't know what annotated here is, it's a uh type hinting thing. Now it's basically saying that the current user what we have here is a user object and then here's some metadata about this user and this user depends on the get current user. You'll see this a lot in paidantic. So for example we could say like this is a string and the metadata about this string is that it is less than or equal to 50 characters something like that. So this creates a convenient alias called current user and instead of writing out the full annotated expression every time we can just use this current user. This makes our route signatures a lot cleaner. So now let's update our postcreate schema and we will remove that user ID here. So instead of that user ID we are just going to pass here. Now postcreate now just inherits from post base which has the title and the content. So the user ID is no longer part of what the client sends when creating a post. So this is much more secure because the client can't claim to be someone else anymore. The server determines the user from the trusted token. So now let's update our post routes uh to use authentication. So within our routers here, I'm going to open up posts. And now first here at the top I'm going to import that current user that we just created. So from O we will import current user. And now let's find the create post function here. So I thought it was closer to the top yet. So create post right here. I scrolled past it. So now let's add that current user as a parameter here. So right after the post I will say the current user and this is going to be that current user dependency that we just created and just by adding this parameter that route is now protected. If someone tries to call this endpoint without a valid token then they'll get a 401 unauthorized before the function even runs. So now we can delete this entire verify user exist block down here. So this entire block here that was verifying if the user exists, we can delete that. We no longer need to look up the user anymore because we already have them from the current user dependency. So finally we can change the post do user ID here and instead that's going to be the current user ID. And this won't be the post do user ID, it's going to be current user ID. So just the ID of that current user. So now this post user ID now comes from the authenticated token instead of the request body. So notice how our code actually got shorter here. We removed about 10 lines of code because we don't need that manual user lookup anymore. So now let's add ownership checks to update and delete as well. So someone shouldn't be able to edit someone else's post. So let's start with update post full. So that is right here. So again, right after our post data here, I'm going to add in a current user and this is going to be the current user dependency there. And now we can delete this entire verify

Segment 3 (10:00 - 15:00)

user exist block here. Now that's not this block here. This block is verifying that the post exists. The user verification is here. So we can delete this entire block here. So now we no longer need that since we're getting that user from our current loggedin user. And now instead after this uh post verification step, let's add in an ownership check. So I can say if the post user ID is not equal to the current user ID, then we want to raise an exception here because we only want the author of a post to be able to update it. So this error will be uh http 403 forbidden and the detail that we'll put here let's just put uh not authorized to update this post. So this checks if the post belongs to the current user and if not then we raise a 403 forbidden. Now why are we using a 403 instead of a 401? So a 401 is unauthorized and that means that you're not authenticated like if there's a missing or invalid token. 403 means that you are authenticated but you don't have permission for this action. And this difference matters for API clients because it tells them whether to log in or whether they're trying to do something that they're simply not allowed to do. And finally we will delete this line that updates the user ID. So I will delete that line there. And that basically does it for our update post full. Now, we also need to do update post partial. It's going to be the same pattern, but actually a little simpler. I'm going to go ahead and copy this ownership check here because we are going to reuse that. So now down here in the function signature after the post data, we will say that the current user is that current user dependency. And after our post check here, we will do our ownership check and paste that in there. Now, lastly, we also have our delete post. It's going to be the same thing. We want the current user uh to only be able to delete their own post. So, we'll put in current user here with that current user dependency. After we do the search for the post, we will do that ownership check right there underneath it. Okay. Okay, so we've drastically improved the security on our post routes here. Now, let's do the same for our user routes. Now, there's something here I want to show you that is pretty cool. Uh, but first, let's update the imports up here. So, first we need current user from O. So, with those O imports, I will add current user. And we can remove OOTH to scheme here and verify access token since we won't need these anymore. And I will show you why. So I will remove those. And now let me show you something really cool about this reusable dependency here. So let me scroll down here to that forward slashme endpoint here. And look how much code that we have in here. So we uh are manually extracting the token. We are verifying it. We are converting the user ID, handling errors, querying the database. So about 30 lines of code. But with our new current user dependency, we can replace all of that. So we can just say here that the only dependency that we need for this route is the current user and pass in that current user dependency. And now we can take all of this code here and we can just say, let me highlight this and delete it. It went a little far there. Sorry, my IDE is very sensitive with scrolling. And now we can just say return that current user. So three lines of code here for this entire endpoint. The current user dependency does all of that work for us. Now it extracts the token. It verifies it. It queries the database and gives us the current user. All we do is return it. So that's a major benefit of reusable dependencies like we've set up here. Okay. Okay, so now let's add authorization to some of these routes here. So one of the routes that I want to update is this update user route here. We want the current user to be able to update their own user profile. So right here after the user update schema, I'll say current user and we will pass in that current user dependency there. And now right here at the very beginning of the function, I will put in an ownership check. Now I don't know if I still have my ownership check copied. So I'm just going to go back there and grab it. And now we can say if this user ID here, so it's not the post user ID is not equal to the

Segment 4 (15:00 - 20:00)

current user ID, then we want to raise a 403 forbidden and we can say not authorized to update this user. So, we're checking right away if they're trying to update their own profile. If not, then we they get a 403 forbidden. The rest of the function, which is the user lookup, the username and email validation, all of that stays exactly the same. Okay. So, now I want to update the delete user route. So, that is right here. So right after the user ID here I will put in the current user with that current user dependency and now we will add that authorization check at the beginning just like we did before. Let me grab that authorization check here and I will use the same one and put that right at the top of the function there. And for the detail we will say not authorized to delete this user. Now we have pretty simple ownership rules but you can see the power of this uh in a more complex application. We could add in you know dependencies for user admin roles and things like that. So there's a lot that you could expand on here. Okay. So now let's test everything before updating the front end because if something's broken here then the front end will be confusing to debug. So first I'm going to delete our current database here so that we start fresh. Uh, so for blog db, I'm just going to delete our current database. And now if the server is running, I'm going to go ahead and restart the server as well, just so that we're starting fresh here. Okay. So, let me refresh the docs here. And now let's try to create a post without being authenticated. So, we can see here that the post now just has title and content. So, we will try this out. test title and test content. Let's execute that. And we can see that we get a 401 unauthorized. So it's telling us that we are not authenticated. So our route protection is working here. So now let's create a user. So up here I will create a user. We will try this out. For the username I will just do Corey MS. For the email I'll put in my email here. And for the password I will do test password one. And now let's execute that. We can see that user was created. Now up here at the top I'm going to authorize and we are going to log in with that user that we just created. Now remember it says username here. I talked about this in the last video. Uh even though it says username uh we are using email here for login. So I will log in there and now let's try to create a post. So test title test content. Let's execute that. And we can see that we got a 2011 success there. And we can see our post there. And notice that we only sent the title and the content. We did not send the user ID field. The user ID field for the post came from our token. And we can see that the author information is correct. So that was from the current loggedin user. So that's great. Okay. Now let's test the ownership aspect of our authentication. So I will log out of the current user here. And now let's create another user and I will just call this test user. And this will be test user atgmail. com. We will execute that. And now let's log in with that user. So test usergmail. com and I had that test password. Let's authorize. And we can see that worked. Okay, I do not want to save the passwords there since those are just tests. And now let's try to edit that first user's post. So I will do an update post partial here for the post ID. Let's do a post ID of one which will be that first user's post that I created. I'll say updated title and updated content. And now we can see here that we are not authorized to update this post because that was created by the first uh query user that I created and not this test user. So we do not get a 401 because we are logged in. What we're getting is a 403 forbidden because we are logged in, but we're just not permitted to update this post because we do not own that post. Okay, so now let's update the front end to send in this authorization header and make all of this work on the front end as well. But

Segment 5 (20:00 - 25:00)

the backend API is working well. Okay, so the first thing I'll do here is log out of that test user. Now, let's go back to our code here. And now for our templates, for the front-end templates, I'm going to open up our layout template, which shows on every page. And now I'm going to update the navbar. So I'm going to look for this loggedin navbar here. Okay, here that is. Now, like usual with the HTML, I have these uh pre-written here. And all of this code will be available for download in the description section below. So here in the layout, I'm going to replace the entire loggedin navbar here. So let's save that. So what we did is we removed the username display and the logout button and we replaced them with this single account link. So the logout functionality moves to the account page and the account button will now show the username. Now right now it's saying account but this will show the username via uh JavaScript. So now let's update the O UI script and I'll replace that entire script block. So again, let me go to my snippets here and I'm going to grab all of this here. So and copy it. Go back to our layouts. And now I want to find where we are updating the UI. That is right here. And I'm just going to replace this entire script block here. So all the way down to this point here, I will remove that. And now we will paste in that new script. Let me get the indentation. Looking good there and get some lines between our scripts. Now, even though we replaced that entire block, there weren't actually a lot of changes in here. All we changed were uh the elements that we're updating with JavaScript. So instead of setting the username display uh we are now setting the account button to that username and we remove the logout handler since that functionality moved to the account page. Now you might notice a small flicker when the page loads. So the navbar starts in the logged out state and then JavaScript updates it. So the server doesn't know who's logged in for template rendering for a split second. Now, there are ways to fix that, but that's a front-end rabbit hole that I won't dive into this series, but that is a common problem with, you know, a lot of single page apps. Uh, and they have ways of fixing that. But for this series, you'll notice that small flicker for a short second. Okay. So, now let's update the create post form handler here and let's walk through the changes here. So, first we need to import get token from our o. js. So right below here I will just say import and we will do get token and this will be from static js off. js. Now we already have this get token in o. js from last tutorial. Now we're just using it here. So next, right after we prevent the default here, we're going to add a token check. So let me grab this from the snippets here and token check here and paste this in. So what we are saying here is if there is no token then we redirect to login immediately. So no point trying to create a post if you're not logged in. And we'll be using this same reusing this same pattern multiple times. And now let's delete this part here. So this is the hard-coded user ID that we've been using. So we have been using that hard-coded user ID of one. We want to get rid of that. We don't need that anymore because the server gets the user from the token. Now in the fetch headers, we want to add the authorization header. So this will be authorization and this will be bearer and then token. and make sure that I spelled things correctly here. So, this sends our JSON web token with the request. The server will use this to identify who's making the request. So, finally, right after the fetch response comes back, we want to add a check for 401. So, that's going to be right here before this if response is okay. So I will grab this from the snippets here and let's put that right above that success check there. Make sure that we get the indendations correctly here.

Segment 6 (25:00 - 30:00)

So if the server returns a 401 then the token is invalid or expired. So we just redirect to the login there. Okay. So that should be all of the changes that we need to make to the layout template. So now let's fix our post template here. So now if I scroll up here to our page, this is going to be in the HTML here. Right here. Okay. So the problem is this conditional here. Let me remove this because we need to use JavaScript here because the server doesn't know who's logged in with our JWT approach that we're using. So what we are going to do is use JavaScript instead. So for this id for this div here, I'm going to give this an ID of post actions. This is for our edit and delete buttons. I'm going to make this hidden by default, which with um Bootstrap, we are just we can just put a display none there. And everything else stays the same. We just removed that ginger 2 conditional and added an ID and that bootstrap class so that JavaScript can control the visibility of these edit and delete buttons. So now we need to replace the entire script block at the bottom of this file. Uh this is a longer snippet that needs ownership checking. So I'm going to replace all of this with a snippet here. So, I'm just going to do the entire script block here. And we will remove that. And now from the snippets here, I will grab our new script block here. And this is the last snippet that we are copying and pasting and save that. And now let's see what changed here. Okay. So, we added imports for get current user and get token from o. js. We added a post user ID variable to store the post owner's ID. We added this check ownership function that compares the current user to the post owner and shows the buttons if they match. For the edit post form handler here, we added in a token check uh and authorization header. We have error handling for a 401 that redirects to login. And we also have an error check for a 403 that shows a error modal here that just says you are not authorized to edit this post. And for the delete form handler, it is basically the same thing here. So we have our token checks. We have our error messages for the 401, the 403, things like that. And let's see, we can see at the end here, I also added this check ownership call at the end to run when the page loads. Okay, so let's save all of those changes there. And now let's go back to our main. py. Now remember, I said that we were adding an account page for the user where the user can see their account information. So let's go down here after the register page. And now let's create this new route. So I'm going to reuse a lot of this here. And we can just say that this is going to be at for/account include in schema is false because we do not want that in our API. This will be an account page. We will put this at account. html and the title for this will be account. Now you might wonder why we're not protecting this route on the server like we did with our API endpoints. The reason is that our token is stored in local storage which is only accessible by JavaScript running in the browser. So when someone navigates to forge/acount, the browser makes a regular git request. It doesn't automatically include the token from local storage. So the server has no way to know if you're logged in when it renders the page. So instead, we handle this with JavaScript on the front end. So when the page loads, our JavaScript checks if you're logged in and redirects you to the login page if you're not. Now, this is just a user experience convenience. It prevents non-logged users from seeing a broken page, but it's not real security. So someone could technically view this HTML if they wanted to by disabling JavaScript, but the actual security comes from the API endpoints. So any attempt to update or delete any account would fail with a 401 because the API requires a valid token. So it's nice to rely on the front end for a user experience type of thing, but it is always on our backend to handle the security. Okay. So now let's create this account template here. Now, just like with other templates in the series, I have a completed template already

Segment 7 (30:00 - 35:00)

prepared that we will paste in and then we'll go over it. So, I will create account. html within our templates here. And I have an account finished ready here. So, let me open this up and paste this in. And now, let me walk through what this page does. So in our content block here, we have a profile section. So this is profile information that shows the current user's username, uh their email, their profile picture, and we can see that we have some placeholder sections here that the profile picture and the password changes are coming in future tutorials. And there's a logout button down here at the bottom and also a danger zone section. uh with a delete account button that triggers a confirmation popup. So if I go down to the script block section here, then we can see that the load user data function fetches the current user from our forme endpoint. That's actually what our current user function here does. And then it populates the display and the form fields. Now if there is no user then we redirect to the login page. That's our client side guard. The update form handler here sends a patch request with our authorization header here and it calls the API there and if we get a response then that's good. Now if we do have a good response then it uses this clear user cache after a successful update so that the navbar refreshes with a new username and the logout button uh it just calls our logout function from o. js JS and the delete account handler that sends a delete request here and then it clears the token and it redirects us to home on success. So there's uh just a lot of error handling and stuff in here just to get that front end where we want it. Okay. So let's test everything here end to end. So let me again stop the server here and again I will delete our blog. DB here and I will now restart the server and have that blog db recreated but it is completely empty. And now let's try out our front-end application here. So we can see that we don't currently have any post. So I will go to register and let's create a new user here. So I will type in a username and an email. We'll just do that test password again and test password and those match. So now it says that our account was created successfully. We get redirected to the login page. Let's try to log in here with our email and put in that test password. And if we log in, then we can see that the login was successful. So now we can see in the top right we have a new post button and we have this account button here that has my username on it. So let's try a new post and I will say new post with new content. We will post that. So we can see that now shows up on the homepage. If I click on this, we can see that it says that I am the author. And now we can update here. New updated post that was successful. So now let's look at this account page. So if I click on this account page, then we can see that we have our account information here. We have a log out button here at the bottom. So let's test that we can update some of this information. So if for my username, I put my last name there instead. Uh let's click update profile. It says it was updated and we can see that it is showing that updated uh profile name. So now if I go to log out, let's create a another user here. So I will call this one test user. We will do test atample. com. And again with that test password let's go ahead and create a couple of posts here uh for this. So I will log in with testample. com with that test password and login was successful. So now if I go to test post with test

Segment 8 (35:00 - 38:00)

content and now let me also do one that will I will make for deletions. So test deletion test deletion. So now I have two posts by this user here uh then one post by that older uh Corey MS account here. If I click on this post, we can see that I do not have edit or delete buttons for this post because I'm currently logged in as test user and I do not own that post. But if I go to one of the posts that I own, then I do have this edit and delete button. So let's try to delete this and we can see that I was able to delete that post. So now let's try to delete an account. So, if I go back to the user account page here, we can see that here at the bottom, we have a delete account button and it says there's no going back. All your posts will also be deleted. So, let's delete this account. We can see it says, are you sure? Uh, big red warning there. So, we will delete this account. It logs us out. And now back on the homepage here, it deleted that test user's post. So now we only have uh the post from that Corey Schaffefer account that we made earlier. Okay. So it looks like everything that we've implemented is working well. So to recap what we did in this video, we built the authorization layer on top of the authentication system from last tutorial. So we created that reusable get current user dependency that protects our routes and gives us the authenticated user. We removed those hard-coded user ids from our schemas and on the front end. Now it all comes from the token instead of those hard-coded values. We also added ownership checks to ensure that users can only edit and delete their own content. And we updated all of the front-end code to send authorization headers with API requests. Now, in the next video, we'll learn about file uploads so that users can upload a profile picture. Uh we'll cover multi-art form data uh image processing on the server side with pillow and complete that profile picture feature on the user account page uh that we saw before. But we've completed that authentication and authorization system. The hard-coded placeholders from before are gone. We have some real security in place. Uh users own their own content. So this is pretty much how a production application works at this point. But there's still a lot of improvements that we can make. But I think that's going to do it for this video. Hopefully now you have a good idea of how to protect your fast API routes with authentication and implement proper authorization checks. In the next video, we'll learn about file uploads and image processing. But if anyone has any questions about what we covered in this video, then feel free to ask in the comment section below and I'll do my best to answer those. And if you enjoy these tutorials and would like to support them, then there are several ways you can do that. The easiest ways is simply like the video and give it a thumbs up. Also, it's a huge help to share these videos with anyone who you think would find them useful. And if you have the means, you can contribute through Patreon or YouTube. And there are links to those pages in the description section below. Be sure to subscribe for future videos, and thank you all for watching.

Другие видео автора — Corey Schafer

Ctrl+V

Экстракт Знаний в Telegram

Экстракты и дистилляты из лучших YouTube-каналов — сразу после публикации.

Подписаться

Дайджест Экстрактов

Лучшие методички за неделю — каждый понедельник