Fast API Tutorial, Part 27: Security with JWT

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hello friends and welcome back to our fast API tutorial this is going to be attempt number two at video 27 which is uh security with oauth 2 password Bearer and JavaScript or Json web tokens not JavaScript Json web tokens okay let's get into it so we've got our app up and running here I go ahead and refresh the page I hope this is uh visible this is not 3000 this should be eight thousand there we go so I hope this is all a little visible um I'm on a new OS it's not um it doesn't it's not windows so I don't know how to zoom the entire thing so I just thought I would make the zoom a little bit bigger so we'll just have to work with what we've got okay so the first thing that we're going to do we're going to examine what a Json web token is so we go to jwt.io and we can see a Json web token is a a stateless means of authentication so it's comprised of three parts here we have the the algorithm and token type this section is encoded with the data that we want to include and this section is the signature okay now if I just start typing in here hello world you'll you notice that this was all changing as I was typing um let's zoom in just a little bit more um hello YouTube so you can see it's changing there now what we're going to be doing is we're going to be instead of using the the token that was just it was just created with the user the username um I forget exactly where it was okay user fake decode token return user get current user uh yeah the access token was just set to be user.username so instead of doing that we're going to actually use something that we're going to have to decode okay so first thing we need to do is install a couple of packages and I'm just going to go ahead and just add them to the requirements because then pycharm will automatically uh install them not automatically but whatever we're going to add python Jose cryptography and we're also going to add in pass lib B Crypt and we're not going to worry too much about what these are just just yet okay why is internet installing there we go install requirement okay now first thing we need to do and I've actually already done it um so it's uh um I've already imported these up here because like I said this is now the second time I'm trying to do this so from pastlib dot context we're going to import Crypt context and then from Jose we're going to import JWT and JWT error okay now what we are going to do here first is we're going to set up the hashing algorithm that we're going to be using or some of the parameters so we're going to set a secret key equals and the idea behind this is this just is supposed to be a a long string and it's not just any long string you're supposed to use openssl and everything like that I don't feel like doing that right now so I'm just going to do the quick brown fox jump jumps over the lazy dog okay and we will set the algorithm to be hs256 and we're going to set access token expire minutes equal 30. okay this is kind of just the beginning sort of stuff don't worry too much about what any of this is just yet you'll see where we're going to use it in a little while now let's set up our fake users DB which is going to be I'm going to use the stuff because that's easier John Doe equals username equals John Doe full name equals John Doe email equals John Doe at example.com hashed password we're going to leave this blank for now and disabled equals false now the hash password before what we were doing is um uh where is it where is it where is it we would just add fake hashed onto the beginning of it which is not a you know obviously not a good hashing algorithm if you can even call it an algorithm um so we're going to actually use a hashing algorithm and I'm going to generate the hash password in just a little bit so let's just move on really quickly so now let's create a token class which inherits from base model and we'll just say access token is going to be a string and token type is going to be a string so this comes this is the same sort of thing that we were just doing up here this is needed for the oauth 2 uh password Bearer via the scheme that we're going to be using class token data is going to inherit from base model as well that is just going to have username which is string or none equals none so this is information that we're going to be storing in the token so you can see here there's some typical stuff there's the the name the initiate the issued at there's the sub this is um uh where we're going to end up passing in the username but you'll see that in just a little bit now we're going to add a user class which inherits from base model username is a string email is a string or none equals none full name is a string or a none equals none and disabled is a bull which equals false by default okay and now we want to add one more model user in DB which inherits from user so any user in DB object will have these four Fields as well as hashed password which is a string okay now we need to set up a password context and what this is going to do is this is going to give us functionality where we can hash passwords we can verify passwords things like that so let's set this up equals Crypt context now there are a couple of things that we need to include schemes is going to stop at schemes is going to be bcrypt which is why we installed nope not that which is why we installed paclib with bcrypt and we're going to say deprecated equals Auto so if we just look really quickly pass limb context it's not the first time I've been looking this up so you can look at all the uh the stuff that's in here if we look schemes list of algorithms the instance of support should support so we're using bcrypt you can use you know some other schemes as well and deprecated equal nope list of algorithm list of algorithms which should be considered deprecated and you can see a little bit down here about what using Auto does I'm not really going to touch on it too much let's just set it like this for our very basic context here now oauth to scheme equals oauth to Pat nope not password request form it's going to be password Bearer token URL equals token now just in case you don't remember from the last video what this is going to do is this is going to give us login functionality somewhere on here and we're going to be able to click a little authorize button in the top right that when we go to authorize it will call this token URL so we're going to have to set this up eventually just like we did in the last one but we'll get there next we want to set up some helper functions So Def verify password what this will do this is going to take in a plain password that we're going to use to log in plain password and it's going to compare it against the hashed password that's in the fake users database and we're just going to return password context dot verify plain password hashed password okay so the the beauty of this so we don't we're not going to be storing um uh plain text passwords in the database that's not a good thing to do you want to store hash passwords but then you wonder how do I check to make sure that a password is valid you do something like this you're going to pass in the plain password which will use the algorithms that you've set and the parameters that you've set and it will hash it and then it will check to see that they're equal okay next we're going to call we're going to set get password hash from a password and we're just going to return password context dot hash password okay again these are these are helper methods don't worry too much about where we're going to be using them all pointed out as we go next we need to define a get user method username is a string and we're passing in a database if username in DB user dict equals DB username and then we're going to return user NDB user dict so all this is doing is we're passing in a value with our database so we would pass in John Doe with our fake users database and if that user is in the database we're going to create a user in DB object where we'll get these four Fields plus the hashed password now we want to call we want to create a method called authenticate user we're going to pass in a database username is going to be a string password is going to be a string and now we're going to get our user user equals get user fake DB and username now if we don't get anything back from this method then we don't want to authenticate the user so if not user return false next if the user exists we want to check the password but if the password doesn't match then we want to return false we don't want to authenticate if the password doesn't match so if not verified password so this is going to be password and then user dot hashed password then return false otherwise we're going to return the user okay that's hopefully fairly straightforward okay now let's um let's set up our token route yeah let's let's create our our our route that will return our token app.post token response model equals token async def login for access token it will take in form data which is going to be oauth to password request form now what we want to do we want to say user equals authenticate user fake users DB form data.username and form data.password so we want to try and authenticate the user first now if we don't have a user or if the user is if we get false from authenticate user we're going to raise an HTTP exception status code equals status dot HTTP 401 unauthorized detail equals incorrect username or password and then headers equals www.authenticate Bearer okay so if we don't if we get false from authenticate user then we raise an exception otherwise access token expires equals time Delta minutes equals access token expire minutes so what we're going to do we're going to create a token that will expire after a certain period of time the way access tokens work with Json web tokens is typically you're going to have an access token that's passed in as a header it's a it's a bearer token that is then verified by any protected routes that you have you will also usually get a refresh token so that once your access token expires you then submit a post request with that refresh token and you get a new access token and sometimes you'll rotate the refresh token and stuff like that but that's generally how how you want to do it we're not going to be creating refresh tokens in this video because that would be exceedingly long so let's just worry about creating an access token so we're going to say access token equals create access token and we don't have that method yet we're going to create it in a minute and we're going to pass in data is going to equal sub user.username and token type error oh no we don't want that that's going to be hold on a second we're going to return access token is going to be access token and here we're going to have token type as bear I read the wrong line sorry sorry about that for here though we're going to do uh what are we going to do expires Delta equals axis token expires and I want this to be like that nope I want it to be like that and I'm going to close off this dictionary here there we go that looks a little bit better so let me get rid of this so we're going to need to create this method here so the idea is that we are going to um get some we're going to pass in this information to our method that will return a Json web token that we will then be able to kind of pass back and forth okay so let's go ahead and create that method def create access token data will be a dictionary expires Delta will be a time Delta or none equals none now to encode equals data dot copy so we're going to be encoding this information in the Json web token payload that we have right there the data that's right there so if expires Delta so if we've passed that in then expire equals date time dot UTC now plus expires Delta else expire expire is going to be date time dot UTC now plus time Delta minutes equals 15. we're just going to set 15 minutes to be the default okay now we're going to want to pass in the expiration to our um to our data that we're going to be encoding here so we're going to say to encode dot update expiration is expire so great we have this this is just a generic dictionary though like we just have this this dictionary that has the data that we're going to be encoding how do we encode it well um I'll do it in two lines encoded JWT equals JWT dot encode so this comes from the python Jose package so we're going to be encoding our dictionary there we need to pass in a secret key and we need to tell the algorithm is going to be the algorithm that we've set up top so here is the secret key and here is the algorithm let's just take a look right here really quickly so you can see claims key algorithm and there's some extra information here so that's what we're doing okay so we have our encoded token return and coded jwc good so now we have an access token great grand now what we need to do I mean we can go back into here we can refresh our page let's try this try it out um John Doe password execute it did not work so this is a different error than I was anticipating um oh no it's it's the correct one okay that's fine um we don't have a password set up for John Doe so what we need to do is we need to we're going to create a password context we're going to open up our python console and let us import Crypt context from passlib dot context import context then we are going to create a password context and then we will just look at PWD context dot hash let's call it password one two three four and there we go we now have a hashed password that we can take this put it there let's make sure it has rerun go to the very bottom and it's showing incorrect username or password and that's because we're setting password instead of one two three four execute and we have our token now I'm just going to show you before we kind of go into any more depth here because like this works and this is good but we want to see how we can protect the route we're going to need to decode the token but let's go into here and you notice right here it's now showing invalid signature I've copied in the token that we got from here I've passed it in and look it shows me John Doe it shows me when it expires at 5 43 eastern time um on May 24th which is today um so this kind of highlights something that's very important that uh you know a lot of people talk about but I'll just reiterate don't pass sensitive data in the payload because this can be decoded like this there's nothing secret about this um you know you you can decode this um with the right algorithm and most of the time you're going to be using this I would imagine I don't I don't know to be honest with you but this is decoded you just get an invalid signature here so if you try and pass this token to the back end with this as your secret key it will not um it will not work but you're still going to be able to see the data so now if we instead though do the quick brown fox jumps over the lazy dog now if we paste our token you can see we get signature verified because now the secret key matches what we've set here okay so that's an important thing to remember don't pass sensitive data in your in your payload okay now let's create a couple of routes to actually you know get user information so we're going to do app.get users me response model equals user async Def get me current user user equals depends on get current active user return current user and we're also going to add in app.get users me items async Def read own items I'm just going to copy this because it's the same thing return Item ID is going to be Foo owner is going to be current user dot username okay now we're going to come up here refresh and it's not going to work because we've not set this I was hoping it would work and we would be able to just kind of try and authenticate really quickly but it doesn't work which is fine let's create the method so first thing that we're going to do we're going to create death uh no we're going to create async Def get current user which will take in a token as a string which depends on the oauth 2 scheme pass and then we're going to say async def get current active user which will return a current user which is a user which depends on get current user there so now it's not breaking at the very least okay so now let's implement this so this is going to be the simpler one so if current user dot disabled raise an HTTP exception status code equals 400. detail equals inactive user otherwise just return the current user simple this is where it's going to get a little complicated what we're going to need to do is take in the token that's passed in via the oauth2 scheme we're going to first try and decode the token then we're going to try and get the username from the token then we're going to try and get the user from the token so decode get the username check to see if the user is in the database effectively is what we're going to be doing so firstly we're going to we're gonna have a couple of unauthorized exceptions that that can get thrown so let's first just create the exception credentials exception equals HTTP exception status code equals status dot HTTP 401 unauthorized detail equals could not validate credentials headers equals authenticate Bearer now we're going to do a try and accept JWT error raise credentials exception so this is baked in so first thing we're going to try and do is decode the token and if you can't decode it using here payload JWT Dot decode token secret key algorithms equals algorithm so if if for some reason you pass in an invalid token where in here it said invalid signature so let's get rid of this let's oh it went away damn it okay whatever where it said invalid signature before if you try and pass in the invalid token then it will throw an error so we want to catch it and then um raise the credentials exception otherwise username which is a string is just going to be payload Dot get sub so if you recall when we were creating the token where is it where is it where is it create access token we're setting the sub equal to the user.username okay so now let's grab the username off of that token so if we don't have a username if user if not username no if username is none then raise the credentials exception simple if for some reason it's not been included then we're good we don't want to um handle that otherwise we're going to set token data equals token data with username equals username okay now we're done with this part now what we want to do is we want to get the user so we first gotten the username so we have user equals get user fake usersdb username equals token data dot username now we're this is Superfluous here like we could just be passing in the username here but this is good for if you want to have extra data in your token data that you're going to be using later this is just kind of setting it up okay you might be wondering why not just pass in the username instead of setting token data that's why we're doing that so we're going to get the user from the username if user is none raise credentials exception otherwise return user now this should all work let's go back into here let's close this because we don't need this we're going to authorize up here John Doe let's try password and let's just open this up very quickly Network and let's try and authorize this returns preview incorrect username or password so what has happened we've sent in the wrong password so we're trying to authenticate which is calling login or it's calling token sorry it's calling token first thing it tries to do is authenticate user get user well John Doe should work let's get rid of the E and see if and and we're going to get the same error incorrect username or password but if we pass in John Doe with an e it goes into here we get the user if not verified password return false password is no good because it was the hashed password one two three four okay so now let's do one two three four now we have our token you can see here is our token okay I'm going to close this so now what has happened let's close this a little bit there we go so we've gone to log in we authenticate the user we've gotten to that we've gotten to the point where we get the user it's passed all those checks we're going to set the token to expire in 30 minutes then we're going to create an access token which itself encodes it which is why we got that encoded access token from before okay now we go into here we can just hit um yeah let's just say try it out execute and you can see we got the information John Doe example John Doe and false um I'm not going to try and mess with the headers that are being sent um you can do that if you're using if you want to use Postman or something like that you can change the token and then you would see that it would try and and fetch this and it would it just wouldn't work you know you can you can put the token in here you can kind of play with some of the data that it's in here and you can see how it would not work and you can kind of follow it through um but yeah that's that's kind of it for for this video um this was a a bit of a long one one of the longer ones that I've done um again uh let's just run black on this really quickly so again we authenticate first which calls on token which gets the user gets an expiration sets data to pass in for the token and an expiration and then we return said token then when we go to fetch this information or we can just fetch this as well just to show you see we get these items here um it first tries to get the current active user which in and of itself calls get user or get current user so first we try and decode then we grab the sub which should be our username then we try and get the user using that username if all of that works then we get our user and we can get the information that we need okay so again a bit of complex sort of stuff but but you know it's when you kind of follow the the code through it's it's it makes a little bit of sense um in the next video we're going to kind of step away from this deep sort of stuff we're going to handle I think uh middleware and um cross origin resource sharing uh I might need to set up a quick little front-end app just to kind of play with it um so you can kind of see how cores would work and not work but yeah that will be in the next video I will see you then
Info
Channel: JVP Design
Views: 6,003
Rating: undefined out of 5
Keywords: fastapi
Id: SKPms69KIco
Channel Id: undefined
Length: 33min 11sec (1991 seconds)
Published: Fri Jul 29 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.