# Python FastAPI Tutorial (Part 10): Authentication - Registration and Login with JWT

## Метаданные

- **Канал:** Corey Schafer
- **YouTube:** https://www.youtube.com/watch?v=Go4wYJJhR3k
- **Дата:** 28.01.2026
- **Длительность:** 57:29
- **Просмотры:** 10,051

## Описание

In this video, we'll be learning how to add authentication to our FastAPI application. We'll implement user registration with secure password hashing using Argon2, build a login system using JWT tokens, and manage configuration with pydantic-settings. We'll also create registration and login pages, set up an auth.js module for managing client-side authentication state, and update the navbar to reflect whether a user is logged in or out. This tutorial sets up the foundation for route protection and authorization, which we'll implement in the next video. Let's get started...

The code from this video can be found here:
https://github.com/CoreyMSchafer/FastAPI-10-Authentication

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

## Содержание

### [0:00](https://www.youtube.com/watch?v=Go4wYJJhR3k) Segment 1 (00:00 - 05:00)

Hey there. How's it going everybody? In this video, we're going to be learning how to add user registration and login to our fast API application. Now, in the last tutorial, we built out our front-end forms with JavaScript. We now have working create, edit, and delete functionality for our post. But if you've noticed, everything in our application is using this hard-coded user ID of one. So right now anybody can create post, anybody can edit any post and anyone can delete any post and that's obviously not secure at all and was just set up as scaffolding for what we're going to be doing here. So in this tutorial we'll build out the backend authentication infrastructure. So we'll do password hashing uh JSON web token utilities and login endpoints. Then we'll create registration and login forms on the front end and wire everything together. Now, this is a pretty big topic, so we're splitting this into two parts. This video covers authentication and the next video will use this authentication to protect our routes and make sure users are authorized to do certain things. So, we're splitting the tutorials up into authentication and authorization. So, authentication answers the question of who are you? and authorization which we'll cover in the next tutorial answers the question of what are you allowed to do. So we need both for a real application and today we're building the authentication layer. All right so let's go ahead and get started. So first we need to install three new packages for authentication. So the first package is pwd with the argon 2 extra for password hashing. If you're using pip, then you could install it with pip install. And this would be pwd lib and then argon 2. Now, sometimes with these extras in these brackets here, they need quotes around them. So, I'll go ahead and do that. Now, you might be wondering why pwd lib with argon 2. If you've watched some of my previous videos, you may have seen me use b-rypt for password hashing. Argon 2 is the more modern choice. It's more resistant to GPU cracking attacks than BCrypt is. So, Argon 2 with PWD lib is the current best practice. Now, as you know, I've been using UV for this tutorial series. So, instead of pip, I'm going to use UV add. I was just showing the pip installation there. Okay. So, we also need the PI JWT package for creating and verifying JWT tokens. So, I'm going to add that on as well. that is pi JWT. Now, PI JWT is the library recommended in the fast API documentation. It has a simple focused API for JSON web token operations. You might see older tutorials use something else, but PI JWT has replaced that in recent fast API updates. And third, we're also going to install uh paidantic- settings for managing our configuration. Now, you might be wondering why we're using paidantic settings instead of something like python. env, which is what a lot of people use and you've seen me use in previous tutorials. Well, pyantic settings gives us several advantages. So, first, it centralizes all of our configuration in to one settings module. Second, it validates types automatically with Pyantic. So if you expect an integer, it'll make sure that you get an integer. And third, it fails fast with clear errors when environment variables are missing or have the wrong type. And lastly, it uses secret string from Pantic to avoid accidentally exposing secrets in logs or print statements. Now, it still loads things from the env file. So, there's no change in your workflow from something like python. env. It's just a cleaner, more modern, fast API approach. Okay. So, let me go ahead and install these. Now, by all means, I'm not saying that the other ways of doing things are wrong. I'm just going to be using Pyantic settings in this video. But if you like something like Python. env, it's still a valid alternative. And if you prefer a simpler setup, uh, then go for that. But I'm just using pideantic settings for this series. Okay. Now, before we start making changes, let's go ahead and I am going to delete my old database here. Now, we're adding a required field to our user model. And SQLite doesn't make it easy to add non-nullable columns to existing tables. In development, it's usually easiest just to start fresh. In production, you would use migrations with something like Olympic. And that's something that we'll see later in the series. But for now, I'm just going to delete our current database. So this blog db file here, I'm just going to

### [5:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=300s) Segment 2 (05:00 - 10:00)

delete this so that we can start fresh. All right. So now let's update our user model. So I'm going to open up our models. py file here where all of our database models live. And we want to open look into the user class here. So right now our user model has ID, username, email, and image file. We need to add a password hash field here. And I want to emphasize that we're going to call this password hash and not passwords because we never store plain passwords in a database. And that's going to be important. So that's not just with fast API, but it's a good habit to get into with basically any authentication system. No plain passwords ever. And I'll actually put this after the email field here. It doesn't really matter, but I'm going to say that we want a password hash. And for the type here, we will do mapped. And this is going to be a string. And let's set this equal to. And I'm going to grab one of my other mapped columns here. So I will grab this. We will do a mapped column of a string. We will have this be 200 characters. We don't want this to be unique. and we will have nullable equal to false. Now, we're using 200 characters, which is plenty for Argon 2 hashes. They're are long strings that vary in length, but 200 characters is more than enough. Okay, so now that we've updated the user model, now let's update our schemas. So, let's open up schemas. py here. So, let me go ahead and open this up. Okay, so we need to make a few changes here. So first our user create schema needs a password field. This is what we receive from the user when they register. So in user create after it inherits from user base we can add that we need a password. This is going to be a string and we will set this equal to a field and we will just do a min length equal to 8. So, we're enforcing a minimum password length of eight characters, which is a reasonable security requirement these days. For a more secure application, you could add some extra requirements here for a password if you'd like. Now, while we're talking about security and privacy, let's also think about what we're returning with user response here. So, we can see that we're inheriting from user base and user base has the email here. So earlier I included email because I wanted to teach clear create response schemas without over complicating things. But while we're here, when someone views a post, do they actually need to see the author's email? Probably not. That's a privacy concern and users probably wouldn't want their emails leaked. So I'm going to replace user response down here with two new schemas. So first we'll have user public for the public facing data like post and then I'll also have a user private response for when the user is viewing their own data which would include the email. So for user response here let's change this to user public and instead of inheriting from user base here let's just inherit from base model. So now this user public will basically be like our base response. So now we can do an ID. We can have a username that gets returned here and we'll set that equal to a string. And then everything else remains the same here. And then we will have a user private. So I will do user private. And this does not contain email here. We will inherit from user public here. And now we will just include that email. And this is going to be an email string. So the user public response has ID, uh, username, image info, but no email. User private inherits from that and adds email. And this means when we return post data, we won't be exposing user emails, which is a big privacy improvement here. But now we no longer have user response. We overwrote that. So down here in post response where we have the author and we were giving that user response. Now we want that to be the user public response there. Okay. So now back to what we were doing before uh we added this password here with user create. Now right here after user update uh let we also need a token schema for our login responses and I'll add this after user update. So we will create a token schema here. This is going to inherit from base model and this is going to have an access token which is going to be a string and we are also going to have a token type and that is

### [10:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=600s) Segment 3 (10:00 - 15:00)

also going to be a string. Okay. So now let's create our settings model. Now remember paidantic settings uh that we installed earlier. This is where we're going to use that. So, we're going to create a config. py file that loads our application's configuration from environment variables. Things like our secret key for signing tokens. Now, it works similar to python. env, but with validation built in. So, over here in my the root of my project, I'm going to create config. py here. And now with this new file, we're going to import a couple of things that we need for our configuration. So first I'm going to say from paidantic let's import uh let's import secret string here for our passwords and things like that for pi from paidantic settings I'm going to import base settings and I'm also going to need to import this settings config dict and now let me create our settings class here. So, this is going to inherit from base settings. And I'm going to mostly just go through and set this up here. And I will talk about this once we are all finished. But first of all, we're going to need this model config equal to this settings config dict here. And within here, we're going to need to tell it where the env file is. We're going to have that atenv. And also env file encoding here. We're going to set this equal to UTF8. And now we're going to create our type hinted settings here. So first for our application we're going to need a secret key. This secret key is going to be a secret string. We're also going to need an algorithm. So I will set this. And this is just going to be a string. And now let's set a default for this as well. For the default, I'm going to do HS 256. And lastly, we are going to need an access token uh expire minutes. And this is going to be an integer. And let's set the default of that equal to 30. And now outside of our class here, I'm now going to create an instance of this. So I'll say that our settings is equal to settings and create an instance of that. And I'm also going to leave a comment here that this is loaded from the env file. Okay. So let me explain what's going on here. So our settings class inherits from base settings which comes from paidantic settings. The model config tells it to automatically load values from av file. The MMV file is for storing sensitive configuration like uh secret keys that shouldn't go into source control. And we'll create thisv file here in just a second. And then after that configuration, we define our settings fields. So the secret key is a secret string which is a special type that won't leak the value in logs or when we print it out. If you do accidentally print the secret key, you'll just see a bunch of asterisk instead of the actual value. you have to explicitly call get secret value on this key to get the actual real string. Now the algorithm defaults to HS 256 which is standard for JSON web tokens and the access token expires minutes uh defaults to 30 minutes. And at the bottom we're creating this settings instance. This loads everything from thev file uh when the module is imported. So we can import and use settings throughout our application. Now this underline that I'm getting here you all might not be getting this uh for me that is likely because yes it's uh thinking that I don't have or it's thinking that I have missing arguments here but we get these from thev file instead. So this is just my rough configuration here trying to warn me actually that is my pi that I can ignore this just by putting a comment here and saying uh type I want to ignore this call arg there and that should get rid of that. Okay. So a broad recap of this. So this config file defines what settings our application needs. But the actual values, especially sensitive ones like our secret key, those come from environment variables or thev file, not from the code itself. So how does pyantic settings here know which environment variable maps to which of these fields? Well, it's pretty simple. So field names match environment variable names and it's case insensitive. So secret key here maps to an alluppercase secret key in our env

### [15:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=900s) Segment 4 (15:00 - 20:00)

file. So for example when we create this env file we will have an environment variable called secret key and that's going to map to that setting there. Now everything in ourv file is going to be in plain text but pyantic settings handles these type conversions automatically. So if I have a 60 as a string in our env file, then it becomes an integer in Python as long as we have this set up uh as an integer here. Now one nice feature is the priority order here. So if secret key is set in your system environment variables, then that is going to win out. And if secret key is not in your system environment variables, then otherwise it's going to use the value from the env file. And if neither of those exist, then it's going to use the default here from our config if one is defined. So you can see that we have defaults con uh assigned for some of these. We do not have a default for secret key. So that actually is going to be required to either be on our system environment variables or within thev file. So this is really nice for production where you might deploy with environment variables but develop with aenv file instead. Okay. So I'm going to go ahead and save this. And now let's create that. Eenv file. So within my project root here, let's createenv. And now let's go ahead and create that secret key. So just like I said, this is going to map to the value that we set here. But this can be all uppercase within here. So within here, uh we are going to have a key. Now in a real application, you'd want to generate a proper secret key. For this, we could either use a temporary secret key or we could go ahead and generate a proper one in the terminal. Let me show you really quick how to generate a good secret key here. So down in my terminal, if I type out python- C, then this allows us to run a command with Python without starting the interpreter. So the command that we can write, we can just say import secrets. And then I'm going to put a semicolon there. And then let's just print uh secrets. ken_hex. And let's do a 32 here. So this semicolon allows us to put multiple statements on one line. So using this secrets module and then this token hex with a 32 that allows us to generate a really good secret key that we can use for our application. So let me go ahead and just use this one and we will update this once we get to production. But you can see we have a huge secret key there. Okay. So let me close down the terminal there. Now most importantly uh env is already in my git ignore file. If I look at my git ignore file here we can see that I have this. env within my git ignore file. So if you're using source control then make absolutely sure that you never commit secrets to git. And this is a huge mistake that a lot of folks make. So they'll commit their code and push their changes to GitHub and then they'll either have their secrets directly in the code or forget to add something like theirv file to get ignore and that pushes up theirv with their code and then everybody can see all of the secrets that they have like their API keys their secret keys and all of that stuff. So definitely don't be one of those people. If you didn't have env in your git ignore then all you have to do is write it out just like that. Okay. So let me go ahead and close my git ignore there. Now another thing that I wanted to note here was that within our config here this env file equal to env this is relative to where your application is run from. So you want to make sure that you're in your tutorial directory when you start the server. Otherwise, paidantic settings won't find yourv file and you'll get an error about a missing secret key. Okay. So let's save that. All right. So now let's create our authentication utilities. So this is going to be another new file here. So again within the root of my project, I'm going to create a new file called o. py. Okay. So these are going to be some authentication utilities here. So first let me add some imports that we need here at the top. Uh the first thing is that we are going to need some datetime classes here. So from datetime I will import UTC and also datetime and also time delta. I'm also

### [20:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=1200s) Segment 5 (20:00 - 25:00)

going to import JWT and from fast API security we are going to import this OOTH 2 password bearer and from pwd lib uh we are going to import this password hash and lastly from config which we just created This is the config file right here. We're going to import those settings. So we can import that settings instance there. So now let me set up our password hasher here. So we can say password_hash is equal to and we can do a password hash dot recommended. And what this does is it creates a password hasher using Argon 2 with the recommended settings. So remember when we installed uh PWD here with the Argon 2 extra, that's what makes this work. The recommended here uh just gives us secure defaults. So we don't have to configure anything ourselves. So now let me set up our ooth 2 scheme here. So we can say OOTH 2. Let me spell this correctly. And then this is going to be equal to ooth to passwordbearer. And now token URL. We're going to set this equal to API- users-token. So this token URL has to match our login endpoint path which will be for/ API/ users/token. So this ooth 2 password bearer extracts the token from the authorization header. So when a client sends that, this schema extracts that token for us. And a nice s side effect of this is that this enables the authorize button in our docs which makes testing authentication a lot easier. So now let me create our password hashing functions here. So first I'll create a function here called hash password and we are going to take a password here as an argument. This is going to be a string and this is also going to return a string. And now we want to return this password hash here. We are going to do password hasht and we want to hash the password that was passed in and no equal sign there. Okay, so that takes in a plain text password and returns the hash. And then we want a verify password function as well. So verify password. The arguments here are going to be a plain password that is going to be a string and also a hashed password that is also going to be a string. This is going to return a boolean. And then we are going to return this password hash again dot verify. And we want to verify this plain password here. And compare that to our hashed password here. So this takes in a plain password and a hashed password and it returns true if they match. If they don't, it returns false. Now you might be wondering why we are hashing instead of encrypting. So encryption is reversible but hashing is not. Uh even if your database is stolen, passwords can't be recovered from hashes. And Argon 2 generates a random salt for each hash. So the same password produces different hashes each time. So that means attackers can't just premputee a list of common password hashes and look yours up. Each one is going to be unique. Okay, so now we want to create our token functions. Now we're getting into some larger functions here. So instead of typing it all out, I'm going to grab some of these from our snippets and explain everything after we add them in. So first, we need a function to create an access token. So from my snippets here, I didn't open these up. Let me go ahead and open these. So from here, I'm going to grab this create access token and then paste this into O. py here. So this function takes a data dictionary and an optional expiration delta and it creates a copy of the data, adds an expiration time, and encodes it as a JSON web token. And I'll likely just call these uh tokens or JWTs from

### [25:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=1500s) Segment 6 (25:00 - 30:00)

this point on. So notice here that we are using our settings. se secret key. get secret value here uh to get the actual string from our settings since we set that as a secret string. Okay, so we have a way of creating an access token and now we need a way to verify an access token. So let me grab this from the snippets here. So verify access token. Let's paste this in as well and go over this. So this function takes a token string and it returns the user ID if the token is valid. So we stored the user ID in the sub field or I should say that we will store the user ID in the sub field when we create the token and this is where we extract it. Now if the token is invalid or expired then we return none. Now let me briefly explain uh JWT structure for those who haven't worked with these before. So a JSON web token has three parts. So it has a header which contains the algorithm and type, a payload which contains our data plus the expiration and a signature which proves the token wasn't tampered with. And all three parts are B 64 encoded and separated by dots. The signature is created using our secret key. So only our server can create valid tokens. Okay. So I will save this. And now let's update our user routes. So within we want to go to routers and then open up our user routes here. And first I need to update our endpoints. So I'm going to add time delta for token expiration. Let me just add these at the bottom and they will auto sort here. So I'll say from datetime we want to import time delta and we also need ooth 2 request or password request form that is going to be from fast API security. We want to import ooth 2 that is going to be password request form. When I just saved that it auto formatted those and sorted them. But we can see that here and here. Uh so let me add the rest of the imports that I need here. So for case insensitive SQL queries I'm going to say from SQL alchemy I want to import I'm going to import funk so that we can do uh case insensitive queries there that imported there. And now I need all of our O utilities. So from Oth I want to import quite a bit of stuff here. So I want to import create access token. I want to import hash password. I want to import ooth to scheme. I want to import verify access token. And password. So if I save that then it'll group all of those together there. Now lastly I'm almost done here. I also want to import our settings. So from that config file that we created, we want to import that settings instance. And lastly for the schemas, remember that we got rid of this user response schema. And we replace this with a user public and a user private response. So I'm going to remove that and uh import those. So we want user public, we want user private and also if you remember we created a token schema as well. Okay. So now let's update some of our routes here. So first I'm going to update the create user route here. So I'm going to change the response model from user response here and this is going to instead be user private. This means that specific user will get all of their user information back after the user is created. Now I need to update the uniqueness checks to be case insensitive. So currently we are checking if the username exists here and also if it is an exact match but really we should be considering for this application we should consider something like Corey and Corey MS. uh we should consider these to be uh the same thing in our application. We wouldn't want one user to have this username and another username. So by lowercasing these we can prevent this. So right here in our wear clause we can use that funk that we imported from SQL alchemy I can say funk. On that username there and for this user username this is just a string. So we

### [30:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=1800s) Segment 7 (30:00 - 35:00)

can do a lower on that. And let's do the same thing for the email down here as well. So for the email say funk. lower on that email and also for the user. We will say lower. Okay. So now let me update how we create the user. So we need to hash the password and be smart about our casing here. So for the username, let's go ahead and preserve the original username casing for display purposes. So if somebody does type in a username like this, then that's how it will display on the front end. Uh we don't want to lowercase it to where all of the usernames on the site are lowercased. But let's store email addresses as lowercase. So here I will say email. lower uh emails are case insensitive by design. So it makes sense to normalize them to lowercase. And let's also add our password hash here. So we need to do a password hash and we want to hash this password before storing it. So we can use that hash password function from our o utilities and this is going to be user. Okay. Okay, so now let's add our login endpoint and this is going to go right after our create user that we are within right now. So right after that function again I'm going to grab this from snippets but I will walk through this step by step here. So let me grab all of this and paste this in and then we will go over all of this here. Okay, so let me explain what is going on here. Okay, so we have our OOTH 2 password request form as a dependency and that handles parsing the login form data. Now, one thing to note is that the OOTH 2 password request form uses a field called username, but we're actually using it for email. So, that's just how the OOTH2 spec works. So, we treat form data username as the email instead. And I've added comments here to kind of help us along. But now we look up the user by email, which is case insensitive. And then we verify the password. Now, here's an important security note. You can see that we're checking if not user or if not verify password. So whether the user doesn't exist or if the password is wrong, then it's going to give a 401 here. But we're returning the same generic error for both of those. So, incorrect email or password. We never want to reveal which one was wrong because that would help attackers know if an email exists in our system. But if everything checks out, then we create an access token with the user's ID as the subject here. And then we return that with the token type of bearer there. Okay. So now I'm going to add a new endpoint here that is going to be this forward slashme endpoint and this is going to be so that the front end can get the current user. Now this is important. We need to put this forward slashme endpoint here before this user ID in the router because fast API matches routes in order. And while the integer type on this uh user ID would catch this, it's still best practice to put specific routes before the parameterized ones. So right after the login endpoint and before this get user route, I'm going to add that me endpoint to get the current user. So from our snippets here, let me go ahead and grab this and I'll paste it in and go over it. So grab this and in our users paste this in. So this endpoint uses that OOTH2 scheme that we have from our O utilities to extract the token from the authorization header. We verify the token and get the user ID and if the user ID is none then we return that 401 unauthorized status. Now here we are trying to convert the user ID to an integer with some defensive error handling here. If it cannot be converted to an integer for whatever reason then we are also raising a 401 unauthorized here. And then if everything else checks out then we look up the user and return their data. And if that user doesn't exist then again 401 unauthorized. Now you might be wondering why we need this endpoint. So the front end needs to know who is logged in. Uh we could decode the token in JavaScript but that doesn't validate it. So calling this endpoint

### [35:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=2100s) Segment 8 (35:00 - 40:00)

validates that the token is still good and gets us the full user information. Uh this is a standard pattern used by a lot of APIs. Okay. So now I still need to update some of these routes here. So I need to update the get user route here to use user public instead of user response. So this is going to be user public here. And let me scroll down here. I also need to update uh this update user route here. For this route I'm going to use user private. Now you'll notice that we're using user public on routes that the public can see and we are using user private on routes where we'd only expect uh the loggedin user to be able to get a response. Now this update user here just like create user it also needs those case insensitive checks for username and email conflicts. Uh so let me update those checks to use uh funk. And also lowercase. So here I'll say if the updated username lower is not equal to the user do usernusername dot lower and also here within the database query I want to do a funk lower on that username and then also a dot lower here after the username. Okay. And now I want to do the same thing for the email. So, same thing here. Just going to put in these lower checks. I could have done this whenever we first created these routes. Uh, but I wanted to start them off as simple as possible and then uh build up as we went. So, I thought that this was a good time to add in these case insensitive checks for our usernames and emails. And lastly down here when we update a user's email we want to store this as lowercase just like when we are creating a user. So for the email I will store that as lowercase. Okay. All right. So before we add the front end for all of this, let's test our API endpoints to make sure everything is currently working. So I believe I have everything saved here. If I restart the server, let's see here. I want to do a uh uv run fast API dev main. py. Let's run that. See if we get any errors. We don't. Okay. So, let's go to our website here and pull this up. Now, if you remember at the beginning of the video, we deleted the database. So, this all should be empty. If I reload all of this, and we can see that it is. So we don't currently have any users or posts or anything. So first let's test out user creation. So with create user route here, let's go to try it out. And I will create a user here. I will do CoreyMS. And for the email, I will just put in my email here. Okay. And for the password, let's just do test password uh one with an exclamation point. Let's execute that. And we can see that we got a 2011 here. So that worked. And we get back our user with an ID, a username, email, image info, but no password. So the password was hashed and stored, but it's never returned. So that's good. So now let's test login. So for this, I want to uh go to this route here, API users token, login for access token. So let's go to try it out. And we can see that we get a form here. So this OOTH 2 form has a username and password fields. Now even though it says username, we are using our email since that's how we set it up. So I'm going to use my email here. And that password was just test password one with an exclamation point. Let's execute that. And okay, we got an incorrect email or password there. So I may have typed that out incorrectly. Let me try that again. Okay, so I must have typed that incorrectly because now we can see that we get back our access token and the token type. So login is working here. So now let me try this authorize button up here at the top. I don't know if you all realized, but now we have this authorize button right here. So, I'm going to click on this authorize button here. And again, it's going to ask me for username and password here. Uh, we are using email as the username. So, I will do this. That was test password one exclamation point authorize. And we can see that logged us in. So, I will

### [40:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=2400s) Segment 9 (40:00 - 45:00)

close that. So, now we're actually authenticated in the docs. So I can test this forward slashme get current user endpoint here. So if I expand this and go to try it out and execute here then we can see that it gives back our user information. So this proves here that the token is valid. Now if I log out and try this route again then it should say that I'm not authenticated. So let me try that. So I'll go to authorize and we can click log out here. And now if I click execute on this get current user route then we can see that it tells us that we are not authenticated. So all of that is working nicely. Okay. So now let's update our main. py file here to update our front-end template routes. So let me open up main. py py here. And let's see down here at the bottom under our user post route here right before our exception handlers. Let's add in our login and register routes. So again, I have these in snippets here. Let me grab these. These are two very small snippets here. Paste these in. So these are very simple templates that just render our login and register pages. And we use the include in schema equal to false here so that these don't show up in our docs since they are not API endpoints. So I'll save those. And now we actually need to create these templates. So first let's create this registration template here. So in my sidebar within our templates here I will create a new template and I will just call this register. html. Now I have the completed and styled HTML file here. Just like the other templates in the series, I've already styled this and put CSS and everything into it to where it looks nice. Uh let me paste all of that into our register. html and all of this code is going to be available for download in the description section below. Now let me walk through the key parts of this template. Now as usual, this series is more focused on fast API than the front-end stuff. frontends can all differ. So, we're mainly focusing on the parts in here that talk to our API, but I'll do a quick overview of everything here. So, we have a form here. And this form has fields for a username, uh for an email, for a password, and for confirm password. The password fields have a min length of 8 to match our schema validation. Uh at the bottom, we can see that we are putting in some JavaScript here. Now, this first bit up here, all of this, this is just uh some password matching logic. This just gives us some nice feedback on the front end if the passwords don't match. That way, the user can see that before sending it off to the API and getting an error. So, that gives us instant feedback as the user types uh showing an error if the passwords don't match. Uh we also set custom validity here. So, the browser's built-in form validation prevents submission. Now when the form is submitted here then we send JSON to this slap API slash users you can see that we are posting to that route and we are sending in all of the form data uh that we gathered from those forms and then on success we show a modal and redirect the user to the login page. Okay. So now let's create that login template. So with templates here, I'm going to create a new file. We will do login. html. I have a finished version of this HTML here. So let me grab this and paste it in here. And again, we can go over this. So in here we have a pretty simple form here within this HTML. Now, one thing to notice here is that this input line here, see how the label says email. Uh, but the name here is username. That's because that ooth 2 password request form expects a field called username. It's just a quirk of the OOTH2 spec, but we're actually using it uh for email login. Now, I could have done this series with username loginins and made it easier on myself, but honestly, I just don't like sites that use username loginins. I always forget usernames. I know my email. I don't always remember my username, and I think most people can relate to that. Okay, so now let me scroll down here to the JavaScript. Okay, so here when we submit the form, we can see that we are sending form data here, not JSON. That's because ooth 2 password request form expects form

### [45:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=2700s) Segment 10 (45:00 - 50:00)

encoded data and using form data in JavaScript handles this automatically. So on success we store the token in local storage. Now let me talk about why we're storing the token in local storage. So it works with all client types. So web apps, mobile apps, uh CLI tools, thirdparty integrations. It aligns with the bearer token standard which is just the common way APIs expect to receive tokens in the request header. Now I should mention the security consideration. Uh local storage is vulnerable to cross-sight scripting attacks where malicious scripts could read the token from storage. So you need to properly sanitize all user input. Short token expiration helps limit exposure if a token is stolen. For browser only apps, HTTPon cookies would be safer, but we're building an API first application that should work on most any client and not a browser only app. Now, another thing that I'm going to do here for the front end is I'm going to create an o. js module for the client side authorization state management. So here within our static folder in our JavaScript directory here, I'm going to create a new file and I'm going to call this o. js. Now let me grab this from my snippets here and we will explain this. So from the snippets here, actually I meant to put the finished version in the project directory here. So I just added that. So let me open this and grab this and put this into our o. js that we just created here. And now let me explain the key parts of this module. So first we have caching to avoid redundant API calls. So we have this current user set to null, fetch promise set to null. Then the get current user function is the heart of this module. If we already have a cacheed user, then we return it immediately. If fetch is already in progress, then we return that same promise instead of starting another request. Now, this is important because multiple parts of the page might call get current user at the same time. And we don't want to spam the API with duplicate requests. Now, we can see here that we're getting the token from local storage. Now when we do need to fetch we call forward slapi slash users slashme with that bear token and if the token is invalid or expired then we clear it from local storage and that makes sure that the user sees the logged out state. Uh but if the response is okay then we just get that current user and return that. Now you might be wondering why we're fetching that instead of just decoding the web token in JavaScript. So calling that for slashme endpoint validates that the token is still good and that on the server and gets us the full user info. So the server is the authority on whether a token is valid. Now the logout function is pretty simple here. We clear the token, clear the cache and then redirect to the homepage. And there we just have a couple of helper functions. So now let's update the layout. html template to use authentication. So let me open up our layout. html template here and scroll up here. So first I need to update the right side of the navbar. So if I go to the navbar here, I have a comment that says that we are on the right side here. Now currently we have placeholder login and register links here. Let me replace the navbar right side section with our offaware version and this was within snippets here. So from within snippets I'm going to grab the right side navbar snippet here. go back to our layout and I'm going to replace from here all the way down to our register route. So I will save that. So now what I changed here is now we have two divs. So we have one for when the user is logged in and out. Now in the next tutorial we'll be updating this loggedin div to have a link to a proper account page. But for now, the loggedin div includes the new post button here, their user's email, and a logout button. And the one for when they are logged out down here. Uh, this is hidden by default. This here has the login and register links. And we are

### [50:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=3000s) Segment 11 (50:00 - 55:00)

using the URL for function to those pages. Now, at the bottom of the file after our dark mode script here, if I scroll all the way down to the bottom here, uh let's see. I'll just put this before our dark mode stuff here, uh before our create post form handler. I'm going to add our ostate management script here. So, I have this in the snippets as well. So, o state management. Let me grab this and I will paste this in here and we'll go over this. So, up at the top here, we are importing get current user and logout from our o. js module that we just created. The update oi function checks if we have a current user. If we do, then it shows the loggedin nav and hides the logged out nav and displays the user's email. If not, then it does the opposite. And we can also see that we're adding a click handler here to the log out button to run that logout function there. And we are also calling update off UI on page load. Now notice here I'm using Bootstrap utility classes like Dnone and DLEX for showing and hiding elements. Uh this is much cleaner than inline styles. So let me save that. And now let's test everything in the browser here. So let me make sure that the server is still running. And it looks like it is. So let's go back to our homepage here. And now let's just try to register a user. So if I go to the registered link here then we can see that we get our registration form. So now I will just create a user called test user test atacample. com and for the password here I will do test password one. Now for the confirm password here it should say that passwords do not match until they actually do. So I have test password one and then when I put that exclamation point then we can see that they match. So now let me click register. We can see that our account was created successfully. It redirects us to the login page. This is just Google warning us that our password is found in a databach. Uh I'm not surprised to that because it's such a uh standard email and password that we're using as that example. So now let me log in here. So that was test@acample. com. Hopefully I didn't spell it incorrectly on the last page. Okay. So it looks like that login was successful. If I close this then it redirects me. And now we can see our email here in the navbar and we have our uh new post button and our logout button. Now [clears throat] let me check the dev tools here to verify that the token is stored. So if I go to inspect here and then let me click on the application tab here and then if I go to local storage and then check here then we can see that we have our access token right here and there it is. So now let me test log out. So if I log out of that account then we can see that local to uh token is now gone. Okay. So I will close our dev tools there. Now one thing I didn't test here was the creation of post. Now all of this uh should be uh the same because we just added authentication here. But let me go ahead and test. I will log in with one of the users that we created earlier and go ahead and test this on the docs side of things. So I will close that. And now for a new post here I will go to create post. Try it out. Now this is something that is going to be updated in the next tutorial. But testing post testing content. And this will be a user ID of one. We still have the I user ID manually added here when creating posts. But if I execute that then that looks good. Go back to our homepage here and we can see that worked. Okay, so let me recap what we accomplished here in this video. So we added secure password hashing with Argon 2. We created JSON web token generation and verification. We built registration and login endpoints on the back end. And we have that forward slashme endpoint for getting the current user. And on the

### [55:00](https://www.youtube.com/watch?v=Go4wYJJhR3k&t=3300s) Segment 12 (55:00 - 57:00)

front end, we created registration and login forms, implemented token storage, and updated the UI to reflect login state. Now, importantly, what's not protected yet? So, our API endpoints still allow anyone to create, edit, and delete post. So, the templates still show edit and delete buttons based on that hard-coded user ID equal to one. All those one values are still there. So anyone can still access any route. But this is intentional and we'll fix all of this in the next tutorial where we cover authorization. So remember at the beginning I mentioned that authentication covers the question of who are you and authorization what are you allowed to do? So, in this video, we covered the authentication aspect of our API. And in the next video, we'll tighten up the authorization and specify exactly what we're allowed to do. So in the next tutorial, we'll create a get current user dependency, protect routes with that dependency, send tokens from the front end, remove user ID from our postcreate schema, and we will replace all of these hard-coded user ID equals 1 values with the current user ID and also add ownership checks for edit and deletes. So that's going to be a big payoff where all these pieces finally come together. But I think that's going to do it for this video. Hopefully now you have a good idea of how to add user registration and login with JWT authentication to a fast API application. In the next video, we're going to be learning about protecting routes and authorization. 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 way is to 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. [groaning]

---
*Источник: https://ekstraktznaniy.ru/video/11660*