FastAPI - JWT Authentication - Secure your Endpoints (Part1 for creating a chatbot with custom ux)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi today I want to show you how to create a chatbot that can provide individual responses for different logged in users we are building a mini fitness app where the chatbot greets a user personally and incorporates age gender and fitness level into its tips the project is divided into two parts in the first part we will create registration and login endpoints with fast API we will then authenticate ourselves the Json web token on a secure endpoint since such a setup can naturally be used for other projects too I want to make a cut there and integrate the chatbot with langchain into the API in part 2 in such a way that each user has a personal user experience ideally you have a little bit of experience with fast API and or link chain for this project I already have a crash course on both topics on my channel which you are welcome to watch in advance before we start with the code there will be a little introduction to authentication with the Json web tokens so we will learn what actually is a Json web token and why authentication with tokens is the most popular method today a Json web token authenticates a user on a server and is sent with an HTTP request in the request header a Json token consists of three parts a header a payload and a signature segment the header typically defines the type of token and the signature algorithm used the header can look for example like this here you've got the algorithm and you've got a type which is of JWT which is an abbreviation for Json webtool the payload segment contains the so-called claims or statements these statements are about an entity typically it's a user and have some kind of additional data for example the name or if the user is an admin or not the last part the signature is what secures the token it is generated by taking the encoded header the encoded payload and the secret key and the algorithm defined in the header and then this creates a hash from it so why is authentication with the jwts no advantages over session authentication for example modern applications normally run as microservice in some kind of cloud environment and you can scale your application for example a rest API horizontally which means you can have multiple instances of the same application this is hard to do and normally you want some kind of fresh API to be stateless so you can use JW Keys these are self-contained they contain all the necessary information and therefore do not need to be stored in a database on a server side this greatly facilitates the scalability of applications so I'm here at vs code as you can see I've got an app folder this is for all of our app logic lived and then we've got the main node Pi this is the entry point for our application so we will build these files step by step and then we have a final application where we can register new users and log in and get a token back so first we're going to build the schema suit Pi this is the file where all of our pedantic models live and the pedantic models are responsible for telling the API how the request body should look like so first we're gonna import some classes and these are the base model class from pedantic we import optional from typing and also enum from the built-in enum module enums are very helpful if you want some kind of categorical variables so let's create our first class this is the user level class which inherits from enum and we create a property like this and the value of the property is then this so our property beginner has got the value beginner and we do it for intermediate like this and for expert like this so we've got three different kind of categories for our fitness application the next class we will create is the user base class and this inherits from pedantic base model this is the same structure like the enum so we've got a property and then we specify what kind of property this should have so we've got an email a username age and the fitness level and we can pass in our default Fitness level like this so we pass in the beginner level as default level here okay from this user base class we inherit again and we add the password category for the user base class and we do it like this because in the API we want the user to pass in a normal password as a normal string but in a database we will have a hashed version of this password so we will create two kind of different classes the database version and the version for the API then next we will create the user in database class and this just adds an idea and the config mode which makes it easier for SQL archemy to convert this into a database class from this user and database class we inherit again and create a user in DB class and this one has got the hash password so again this is the normal password for the API and this is the class for the database with the hash password okay now we are done with the API classes and database classes and now we can create the classes related to tokens here we get a new class called token data and we inhabit again from the base model we pass in one attribute which is the username because we want to store some information in the JWT then we will create the token itself so we store the access token property here as string and we also store the token type which is in our case a bearer token which is the most popular type of token so first we'll import some functions from SQL Alchemy for example they create engine functions or the session maker and then we will create a database URL we will just use equal light so we will create a test.db file which is in our current folder here this will be created when we start the app and with this information we will now create the database engine we pass in the database URL and also some connect arcs which is only needed for sqlite then we've got our engine okay then we will create a session class and this is done via the session maker function we pass in some arguments like the bind argument where we have to pass in our engine here and then we will also create a new base class this is needed for creating database models we just have to call the declarative base method here to actually create the space class now we can create our getdb function this will always create an instance of a new session so we always have a new session when we make a connection to the database we won't have any raised conditions this method here will be used as dependency injection in our API later we can now use the classes we created here and go to our models.pi and actually create our database tables here so we will first import some classes from SQL Alchemy to build our tables for example the enum or the column class the string and integer to Define what kind of types a column will have and then we will create the Base Class here this is the class we will inherit from to actually create a table we also have to import the user level class here because we will use it here with the SQL enum and then we will create the class this class called user inherits here from the base class and gets a Dunder property called users so this user class name here is just for us for development but this under reward will be defined in the sqlite database so first we will create an ID ID is always needed inside a single table and we set it here as primary key in our table it will be a normal column so one two three four and so on we then will create a username column and this column will be a string and it should be unique because we will pass this username information inside the jailable tier to actually Define or find out who makes the request so you can't have duplicated values inside of this column then we will create the email it should also be unique because you only want to register with an email once okay we will also store the password of the user in the database but very important we will not store the plane password we will store a hashed version so uh not human readable version of the password so if our database gets stolen for example then the user data is not in danger the potential Thief could not do anything with the hash passwords okay then we will store some user based information for example the H with which is just an integer it is not a primary key or anything and then again we will use the level here and this will make use of the enumclass and we use our user level enum here okay fine that's our final table and we can now build the logic to actually register and log in okay now we will create the logic to actually create a Json web token we go to the security.pi file again we have to import some modules of course like Crypt context class or the JWT module here from python Jose then we will create three constants the secret key the algorithm and the access expiry minutes this is for the token validity so it's only 30 minutes long by default and the secret key should normally be of course a secure value but for our purpose here it's not important just keep in mind that this is not the best value for a secret key of course okay then let's instantiate the Crypt context class we can now use the PVD context instance here to actually create a password hash we pass in a single string our password and this gets passed to the hash method of the PVD context class so this function here returns a hashed version of our plain password okay now let's create our create access token function this gets an argument here which is data so we can pass in some data from the database here to this function and we extract for example username and pass it in our Json web token it also got an optional parameter here which is the expires time Delta since data is a dictionary which makes it a reference type we want to create a copy first to not manipulate some kind of single instance in memory and get some weird Behavior so we copy the data here call it to encode and then we check if we've got some Delta here which is by default none and then we will just create the expiry date so if we don't have any arguments here we pass in just the time Delta of 15 minutes okay now we update the to encode dictionary and pass in the expiry date then we will run the encode function of the JWT module this will create a JWT we pass in the dictionary as data so our payload here then we pass in the secret key and we also pass in the algorithm all these parts are needed to actually create a JWT and then we will just return the encoded JWT from our function so now we've got the logic to create an access token but we of course also want the logic to retrieve information from the access token so we can do this in the outside Pi we will first import some classes again a very important class here is from Fast AP security the OS 2 password Bearer class this makes it very easy to work with jwts in fast API I'm sure you to do so so we will first create an instance an or2 scheme from this class this token URL is responsible for actually returning a JWT and this oauth tool scheme allows us to use this token in some kind of dependency injection in fast API then we're gonna use it in a few seconds but first we will create some kind of helper functionality which will also be a dependency injection so we will Define the get user function here and what we're going to do is we pass in a username and we want to actually retrieve the username from the user table so we need this because we want to extract the username from the jwtm and then we pass it to this function to actually retrieve the correct user from the database with this function we can now create our get current user function this will be also used as a dependency injection and we need the token which we will inject in this function here with the depends class and we pass in the OS tool scheme here which will retrieve the correct token from this URL and we also pass in the session object from the get the DB function which we defined here in the DB module Okay so we've got the token and ddb and first we will create some kind of exception which we want to raise if we are not authenticated correctly then let's now create the actual logic and we will use a try catch statement here and we now use instead of encode we use the decode function from JWT and we pass in the Json web token which we retrieve from this token URL and we also pass in the secret and we also pass in the algorithm so only with this secret and with the algorithm it's possible to decode the token again so now we've got our payload here and from this payload we want to extract the sub key this is the default key and we can extract the username from it if we don't have this username we want to raise the credentials exception here but if we've got a username we want to create a new instance of the token data and pass in the username here and then we've got here our token data instance if that didn't work we will listen to the JWT error exception and then raise our custom exception here to the API okay now let's run the get user function again we Define that here and we pass in the current session and we also pass in the username which we stored in the token data class so we access it in this property and this Returns the database object so the workflow is we extract the username from the payload of the token and then use this username to actually make a query to our database and to retrieve the database object and if we've got a user here then we want to return the user itself otherwise we can raise our credentials exception first we have to import some modules of course and we import the API router from Fast API and here we also import every other module where we created our logic to create authentication our schemas and also the security logic so this gives us access to all functions and objects from these modules so first let's create our API router and this allows us to use The Decorator here and we will create a post endpoint which we will call register and we Define a response model this is the user NDB base model here in the schemas class this is our response model and now we will create our function which we call register and what is passed here is defined in the user in model so we want to pass in email username age and level and password these are the five attributes we want in our request to the API and then we also use dependency injection to always get a new session for our database so now let's create our logic first we will run the get user function we pass in our session from the database and we pass in the username which is the user in attribute here to actually see if we've got a user already in the database if that's the case we want to raise an HTTP exception username already registered we pass in a status code 400 if we don't have this username in the database we again want to do this for the email and again also want to run an exception because username and email have to be unique here if that's correct we will take the password and pass it to the get password hash function so we will convert the password in a hash password and the hash password will be used to then store all the relevant user information here we exclude from the user information dictionary the normal password and pass in the hash password to it so we will create an instance of the DB user here and now we will run the add method and add this DB user instance to the current session then we will commit the session and then we will just refresh with the current object here and everything is set up correctly in the database okay at the end we just have to return the database object from the API okay now let's create the login endpoint or we just call it token endpoint this slash token has to match the token URL inside our auth.pi file in this endpoint here we use form data as some kind of attribute and this all odd password request form will actually create some kind of request form and we can extract the data entered here we will have a username and password attribute of this class we can use it like this we run again our get user function pass in the current session and we pass in the username by extracting the username attribute from this form data object okay now we've got the user or we don't have a user if we've got a user object we want to check if our hash password equals the password from the form data so there is the verify method from the PVD context object and this allows us to compare a hash password with a plain password and this will be turned true or false so if this is false or this is false what we want to raise an exception so we raise an HTTP exception here unauthorized so incorrect username or password if everything works fine and we've got the username and the hashes are equal then we will create a new time Delta this time that the object will be passed into the createx token function this is the option argument it normally if we don't pass it here it just gets 15 minutes so now we pass in the data and we pass in the sub key this is later used to extract the username again here here we gonna edit so we add this is dictionary and here we extract it again in the payload so we retrieve the username like this and we add the username like this okay fine now we have created our access token and then we can just return the access token we do it like this in a dictionary format with the access token itself and also the token type which is of kind better okay now let's create our third endpoint we will call it conversation because in the second part of the video we will create an actually chatbot interface for this endpoint in the first part of the video we will just return the current user and this is done by using a dependency injection and here we use the get current user function we pass it here and then we've got our current user object so there's a lot of stuff going on here again we will extract the token and then we will run the get user function run it here on the database and then at the end return the user so we can just use it like this in the API rod and only call this method once via dependency injection and we now have the current user in our endpoint and we could of course do this for all of our other endpoints too so very nice so what we're going to do here is only return our fake conversation this is a secure conversation and the current user which is the current user and again this is the class here so we have to access the username attribute by this dot username and this will return the current logged in username okay now we can create 8 or entry point our main.pi let's now go to the main.pi and here we can just import the fast API class we also need UV corn to actually run our server and then we need the engine and also the router from the routes file now we will create a fast API instance call it app and then we will use the Base Class here from the models and run the create all method here and we pass the engine so this is responsible for actually creating our database and all of our tables and then we will include the router via the include router method of the fast API instance at the end we just have to run ubcont.run pass in the app argument run it on localhost and pass in the port 5555 so if you run python main.pino this should work and we should see our app as you can see our test.db file gets created and we can now register and log in users okay now let's try our API first we will try to connect to the current user endpoint and just execute here as we can see we are currently not authenticated we get the correct 401 Response Code first we'll register a new user here here the email doesn't matter we will just call it for example test user and then execute here and then try to get a token here first with the username test user and the password was the default so it's called string and this allows us to get back the X token so as you can see the X token looks like this and the token type is better so before we gonna get the current user I will show you something and on the website jwt.io you can pass in our Json web token and try to decode it we don't have currently the correct secret as you can see here we've got the invalid secret this is only stored in the server thread so we would have to pass it here but as we can see even without the valid secret we get our correct sub so this is the user we pass in here in the create access token function so you shall never really never ever store some sensitive data here username is fine but never store any passwords or credit card information or anything in a Json web token okay now let's log in here here we have this key icon and we can just log in with test user and string let's authorize here and now we get the JWT here in the request header on every request we make so if you try the conversation endpoint again this is just a single get request we're gonna make here here we can see the token gets passed in in the request header it's of type Bearer and this is the actual value of the token and now we can see this is the secure test conversation and the current user is test user okay perfect as you can see this works the app does what we expect from it to do and now in the second part of the video we can now create our secure and personal chat endpoint see you bye bye
Info
Channel: Coding Crashcourses
Views: 1,193
Rating: undefined out of 5
Keywords: fastapi, langchain, openai, authentication, jwt, json web token
Id: O0qs1uRd1K4
Channel Id: undefined
Length: 23min 2sec (1382 seconds)
Published: Sat Jul 01 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.