FastAPI JWT Tutorial | How to add User Authentication

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everybody and in this video we are going to learn how to use authentication and authorization using jwts which is Json web tokens using fast API so we're going to be able to create a token pass that token into our Swagger and API endpoints for fast API and then be able to decode that JWT so we get authorization in our application so let's start by going through our application so we have our main.pi file and our main.pi file has our fast API import with fast API status depends in HTTP exceptions we have our models which we're going to go through here in a second we have our database which has an engine in session local for SQL Lite if you're not sure about sqlite and how we can create databases um you can check out the video I've already created we then have from typing import annotated and then from SQL alchemy.orm import session we start a fast API application we are binding and creating all the models and then we have dependencies for our database and dependency injection where we have one API endpoint that just fetches the user but since we don't have authentication yet this will not work if we look at our models.pi file we can see that we have a table of users which takes in an ID a username and a hashed password and the hash password is very important in this aspect because the password in the database will be encrypted now this will protect you for security reasons if your database gets corrupted or stolen the user's emails might be taken but the passwords won't because we have them encrypted and then our database.pi file which is simply creating a URL route to our SQL Lite where we create an engine a session local and a base so let's go ahead and just start creating our Authentication all right let's open up our application real quick just so we can see what it looks like inside here we have one API endpoint where a user needs to be passed in if we pass in a user right now we're going to get an error because we don't have the correct validation for our user and that's kind of because we have it checking for null or none but we also don't have authentication set up so let's go ahead and do that start by turning off our terminal now we also need to go ahead and import three different dependencies the first one being pip install python Jose and then in Brackets we need to type in this we then need to install our passlib.bcrypt and then we want to pass in PIP install python multi-form now with all of these we now have all three dependencies that we need and just to kind of reiterate we have fast API installed you've Acorn installed SQL Alchemy installed and now we just installed these three different dependencies that we need which makes six dependencies total for this application now the very first thing I'm going to do is just go ahead and create a new file of auth.pi in our inner off.pi file we need to go ahead and import these dependencies we have from date time which is time Delta and date time which we're going to be using for the expiration of the JWT we have typing for annotated we're saying from Fast API we want to import our API router our depends and our HTTP exceptions we want to say from pedantic import based model for our data validation of the user SQL Alchemy orm and our session our Starlight status for us able to return the correct status code back to the user we have our database our models which has our user table our password.contacts for our Crypt context and our fast API security for oauth 2 password request form and oauth 2 password bear that's what we're going to be passing in and then from Jose we need to import JWT and JWT error now the next thing we need to add is our router equals API router and I'm going to have it as a prefix of Slash off with a tag of off this will separate it from our main.pi file the next thing that we need to add is our secret key and algorithm our secret key can really be anything we want I have a arbitrary list of numbers and characters but this is going to be the secret that will be able to identify and decode your JWT I also have an algorithm a standard algorithm is the hs-256 we then want to add the decrypt context which is equal to Crypt contacts where we pass in our schemes of bcrypt and deprecated equals Auto this is what we're going to be using to do a lot of our password hashing and unhashing so we definitely need to keep this dependency injection for B Crypt contacts we then need to add our oauth 2 Bearer which is equal to our oauth 2 password bear with our token Euro which is equal to auth token now it's off token because token is going to be an API endpoint that we create in a little bit while auth is this file specifically so here we can say router equals API router with a prefix of off that is going to match this off and then later on we're saying slash token which is going to be the API endpoint that we're passing in we then need to create our user request of base model this class is going to be the pedantic data validation that we are testing the user on the registration so when a user creates a new account of username and password we are going to be using our create user request to validate it before we submit it to the database as a new user we then want to create a class of token then we have an access token which is a type string with a token type that is also of type string we want to make sure that we use this token because Swagger and fast API kind of does some magic behind scenes we need this token validation because on Swagger we'll be able to log in and type in a password and a username to be able to authenticate a user and to be able to do this we need to make sure that we have a token class so right now we have our create user request and our token class we then want to make sure we have our dependency for database so our def gitdb which has a DB that's equal to our session local we say we're going to try and yield our database and then always no matter what whether the try failed or was successful we want to make sure that the database is closed and then lastly we want to create our database dependencies so our dependency injection is going to be database dependency which is equal to our annotated that we pass in up here from typing and then we want to say depends on our get DB all right awesome stuff now the very first thing we want to do is go ahead and create a router for DOT post what we're doing here is we're saying at router.post so we're saying at this empty API endpoint we want to return a status code of status dot HTTP 201 which is the created status code for application we then want to say async def create a user so that's just our function name where we're passing in one which is our DB DB dependency which is our dependency injection for our database and then create user request of our create user request base model pedantic object that we passed above here we then want to say create user model equals users we want to create this model based on the create user request that's coming in we say we want this create user model to equal users but inside here we want our username to equal the request that we get back to username and we want the password to be that hashed password so we can do this by passing in username that is equal to create userrequest.username but then we can also say hashed password equals our bcrypt contacts and remember we pass that up here using our Crypt contact where we pass in bcrypt and deprecated is equal to our Auto and then we say dot hash and that's how you Hash a password so we're just going to say hash and then we're going to pass in our password and then this function is going to make the password unrecognizable also known as encrypted and we're going to assign this to our hash password we're going to add this user to our database and then we're going to commit the database this might be thinking well if a password is encrypted and hashed how do we validate if a user is authenticated or authorized and overall just logs in successfully now a hash password let's say you have your password as test1234 and you want to encrypt it now there's going to be a ton of letters numbers and symbols that you may not know but what we do is we pass in the password we hash the new password and see if it matches the encrypted password we already have in the database so if the encrypted password matches the other encrypted password that means you're successful if not that means the user typed in an invalid password or username so now that we have this let's go ahead and just try and run this by typing in UV corn main colon app dash dash reload let's open up our application I'm going to refresh the screen we don't necessarily want to use this yet oh you know what we forgot we forgot to add our API which is our off.pi file in our main.pi file and we're using routers for this so the first thing we want to do is right under our app equals fast API with a app dot includes router and in here we want to pass in the file that our authentication is assigned to so it's assigned in auth.pi so we want to pass in off and once we have our off in here we can see that it's going to be a router so we want to add our off dot router and we're going to get a error here because we haven't imported off yet so let's just go ahead and say Import off so now that we added off as a route within our application when we refresh the apis we can see that under our off tags we have a create user now if we open this up and say try it out I can say username is coding with Ruby where the password is just going to be test1234 and I'll add an exclamation mark for fun we can click execute now if we scroll down we can see that we get a 201 which is a success for a created item and a null response body so technically inside our database right now which is our to Do's app.db because I'm using sqlite has a user of coding with Roby with a password of test1234 exclamation mark but that password is again encrypted in the database all right now let's go back into our off.pi file and underneath our last API endpoint of create user let's go ahead and just add a new route of Slash token where we create a response model of the token based model pedantic object that we've created right here where we can say async def login for Access token where we pass in a form data of annotated oauth2 password request form of dependency injection and what this is is this creates the form that we need to be able to successfully pass any username and password we then want to pass in our database of dependency injection and we want to say user equals authenticate user where we pass in our form data.username and our form data.password and our database now one issue we're going to know immediately is our authenticate user method does not exist so that's what we want to create right now so right under this I am just going to say deaf authenticate user where we pass in a username of type string a password of type string and a database object which is our database dependency injection that we patched into this API endpoint here we can say user equals db.query of users dot filterusers.username equals the username that we get from the request DOT first so we're searching through the sqlite database and trying to find a match on the username that's getting passed in through the request and the username that's already saved in the database once we find a match that's good so what we're saying is if there's not a match of a user we just want to return false so user is going to be returned as false and then we want to check the bcrypt context.verify now what verify does is it verifies that the passwords match so what it's going to do is it's going to take in a first variable of password it's going to Hash it behind scenes and compare that password with our user dot hash password that it finds in the database so again if user is found we want to make that our user with our username and password but the password is going to be hashed and compare it to the password that's getting passed into our slash token API endpoint if there is no match here we want to return false so if the username fails false or if the password fails false but if there is a match on the username in and the hash password we want to return true so here user will now have data of username and a password so here this user is going to have our username our hash password and our ID so the next thing we need to do here is say if not user so if user is false because it failed the authenticate user we're going to raise an HTTP exception where we pass in a 401 unauthorized with a detail could not validate user if the user is successful well then we want to create a token and we're going to be creating our create access token where we pass in a username a user ID and a Time Delta of 20 minutes now A Time Delta means how many minutes in the future so this JWT which is this token this Json web token is going to be valid for 20 minutes in the future so what this means is after 20 minutes either the user is going to have to re-authenticate themselves or we could potentially add logic behind scenes that each time that that specific token makes an API guy endpoint request we can add time Delta therefore you're not going to be always logging out but for this use case we're just going to say hey after 20 minutes you always have to resign in all right so now let's go ahead and create our access token which takes in a username a user ID and a Time Delta now this is a unique situation because now we're going to be creating the actual JWT that we're going to be using from the server and our client so we're going to say def create access token where we take in the username user ID and time Delta that we're passing right here and we want to say encode the sub as the username and the ID as the user ID so our JWT once you decode it once we say hey this is a user let's decode this JWT inside the JWT is going to be the username for that user and the ID for that user we want to say it expires whatever time it is right now plus the time Delta of 20 minutes we then want to say hey in our encode which is our JWT we want to add an expiration date with something called EXP T which is going to be our expiration so we know when that JWT is expired and then we want to return a JWT that is encoded where we can pass in the encode the secret key and the algorithm the way you validate a JWT is three really things there's the encode which is going to be the data that's inside there's the secret key and then the algorithm and if we scroll all the way up to the top when we create the JWT we use a secret key with this string we use this algorithm and we are encoding that data that we passed in when we decode it the application needs to know the secret key and the algorithm and that's what makes jdbts so powerful with security is that no one else can hack your GWT unless they know the secret key and algorithm so make sure these two variables are hidden within your application so other people cannot identify what your secret key and algorithm is alright so after we do this let's go ahead and save the application let's go back to our browser I'm going to refresh the page and let's go ahead and just get a demo token for our application so we know the user is coding with Roby [Music] and the password is test1234 exclamation mark I'm going to hit execute and here we can see we get a status response of 200 with an access token of this JWT [Music] with token type of bear now here's a fun thing you can do you can go to JWT dot IO and here is an example of an encoded GWT and a decoded JWT so we can just erase everything in here and paste in the jdbt we just got from our application and see that the algorithm is hs256 type is a JWT the sub is coding with Roby with an ID of 1 and in the expiration of this JWT now everything inside here is called our payload while everything above here where I was talking about the type in the algorithm is the header so now what we want to do is create a method that can decode the JWT so we can use it in the future so let's go back to our Visual Studio code and add a new method right here under our create access token where we can pass in our git current user where we pass in a token of annotated string depends oauth to bear so our git current user is going to take in our JWT once it validates that hey this is correct we can use payload equals jdbt dot decode where we pass in our token secret key and algorithm and now this is going to do something if this fails this jwt.d code where we pass in the algorithm and secret key it will fail to this exception this exception is the JWT error where we raise an HTTP exception of a status code of 401 unauthorized where we say we could not validate the user if the payload is successful well here we want to say the username is going to be equal to whatever is in the payload dot get sub the user ID which is going going to be an INT is equal to the payload dot get ID if username is none or user ID is none we of course want to throw an HTTP exception of a 401 that could not validate the user and then else always return the username of username and an ID of user ID now that we have this created we do not want an API endpoint let's switch back to our main.pi file where we have this one API endpoint of at app.get with an empty path which returns a 200 if successful where we pass in a user a DP dependency and if the user is none we throw an HTTP exception or we can just return the user so right here right under auth just to make it easier I'm going to say from off import our get current user which is going to be our kit current user method that we just created which decodes the JWT we then want to scroll down and create a new dependency of user dependency equals annotated of a dictionary where we have dependency injection on our get current user so then when we say our get user right here on our user we can say of type user dependency which will automatically take the request search the header and validate the header of the JWT token that's passed in or we can say if users none raises exception or just return this user so now let's go back to our application let's go ahead and just refresh the page now our get user API endpoint has this little lock it did not have this lock before we can click this lock type in our username well first we can actually just go ahead and say try it out if we try it out we're going to get a detail of not authenticated with a 401 unauthorized status code if we pass in a lock and we say the username is going to be coding with Ruby with a password of test1234 exclamation mark and we click authorize we can see that it did in fact authenticate us and it was successful so now if we go ahead and say execute after logging in we can see that we pass in automatically our JWT and our users return to us as username coding with Ruby with an ID of one alright and that's it that's how you quickly add authentication and authorization using Json web tokens to your fast API application and I will see you in the next video
Info
Channel: Eric Roby
Views: 45,130
Rating: undefined out of 5
Keywords: fastapi, python, fastapi jwt authentication, fastapi jwt, jwt, jwt python, fastapi tutorial, fastapi python, fastapi oauth2, jwt token, oauth2, jwt authentication, fastapi authentication tutorial, json web token, fastapi authentication, json web tokens, user authentication, authentication, python jwt, python jwt authentication
Id: 0A_GCXBCNUQ
Channel Id: undefined
Length: 20min 27sec (1227 seconds)
Published: Sun Jul 16 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.