Creating a RESTFul API in Flask With JSON Web Token Authentication and Flask-SQLAlchemy

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone its Anthony from pretty-pretty here in this video I'll be showing you an example of an API a restful api that has some authentication and uses flat sequel alchemy so since this video so long I'll show you first what the API does and then I'll get into actually building it so the API has two parts - it's a 2d API so it has a list of users and it has a list of - dues for individual users so if I go to slash user here and send I get a message saying that the token is missing and that's because it requires some authentication so I have to go to the login route and specify some authentication so my particular user is admin I'll update the request and send it I'll get a token in return so I'll take that token and then I'll send a request to the user route again but this time I will include a token so he is X access token and the value will be the token that I just copied then I can send to user and then I get a list of all the users in the database with this I can create new users so by posting to user I'm able to create a new user if I use adjacent object so I'll switch to raw here and Jason and a name will be pretty printed and password will be and I need quotes around this password so I'll send that tells me that new user has been created I'll go back to the user route and I'll see prettyprint is there and in addition to viewing the users I can view an individual user I can also view to do so let me go over to to do and send and I get nothing because my particular user the admin user doesn't have any - duze but if I create one by doing a post on to do and change this to be text and I'll say record a YouTube video and I need to spell video correctly I'll sin that tells me that to do has been created I'll get the list of to do's and I see record a YouTube video is there and I see complete is false so if I put on number 4 it tells me the to do has been completed I'll get the list of two dudes again and I see complete is true so there are other things that are used in this API other routes and methods and I'll show you that as I build it but this is a basic overview of the API so it's basically a to-do API where multiple users can have their own to-do lists and an admin user can administer those users and create nd8 delete users so before I give in to writing the code I just want to mention that pretty printer calm now has flash courses and I'll be adding general Python courses as well I'm actually working on a course for Python requests so if you're interested in signing up for any of the courses go to pre printer comm I have both free courses and I have a premium course up and I'll be adding more courses over time so now that we have the demo out of the way let's get to writing the code all right so let's get started with the app so I'll start with a standard flask import and I'll set everything up quickly here and this is going to be more code than I usually write in my videos so I don't know how long this video is going to be but I'm probably about two hundred lines of code I'm guessing for this entire thing to work so I'll need a secret key to use the encoding of the token so secret key this is secret and then I'll also need to configure a database so sequel lights or a sequel alchemy database URI it's going to be here on my machine so sequel lights and then mount C users Anthony documents let's see API example and then I'll call it to do DB so next I want to import flask sequel alchemy and then once I instantiate this I can start creating the class so pass app to seek welcoming so pretty easy stuff so far so now what I want to do is I want to create the two tables or the two classes that represent the tables in the database I'll have a user table and I'll have a to do table so obviously the user table will hold the user information and the a to do table will hold the to do information so the first table is going to be the user table so I need to inherit DVD model and I'll have a few columns in here the first will be an ID column standard ID column in integer and this will be the primary key and then the next column will be a public ID column and I'm using a public ID because when I put the public ID in the token you'll be able to see it if you decode the token and I don't want the IDS to match up exactly with the sequential IDs in the database because then someone would know how many users are in the database and they can just simply supply the next number or the previous number so to make it a little harder to figure out the users I'll use the public ID so this will be a string of 50 characters and I'll be generating it from a youíd and I'll show you how to do that later on each user will have a name so DB column and then DB string again a password for each user DV string say 80 and finally an admin column which will be either true or false meaning if it's true they are an admin and if it's false they are not an admin so the next table will be the to do table and it will in here from DB model again and this will also have an ID column in this case is not really important to have a public ID so I won't have one so primary key is true and then a text column DB string let's say 50 and that will represent the actual text of the to do a complete column which will be a boolean meaning if it's true the to do has been completed if it's not true then the to do has not been completed and finally a user ID and this user ID will be the same user ID from the user table I'm not going to add a foreign key just to keep everything simple but you could if you want to so that's it for the tables in the database so what I want to do now is open up Python and from I believe the file name is AP I going to import DB and now I'll create the database so I like set out a Python and I'll open up sequel I three to do and let's see if I have the tables there I have to do in user which is exactly what I want and since there's no data acting even query so I'll close that out and I'll start my app now just to get started everything so the first thing I want to do now that I have the database up and running is I want to handle the routes for the users so the user routes will only be accessible by admin users and they're basically used to see other users that are existing at the database to create a new user and to delete users if you want to so first I'll create the routes without having any information in them and they're all going to be on the slash user in point now add methods get just to make it clear here so I'll call this function it gets all users and I'll return nothing for now since I'm just doing the outline of this so I'll have a user in a particular user ID so I'll call a user ID here and methods will be get again and this will be called get one user it's going to return nothing and then I'll work on the final route so user this will be postal methods posts this one will be for creating a user so create user return and then I'll have one for put so this will take in a user ID methods will be put and the name of the function will be promote users so this will promote any user ID that's passed in to an admin user so I'll return nothing again and then finally the last one for the user in point will be delete so takes in a user ID and the method will be delete and I'll call this delete user and return that so pretty easy so far and as we'll see pretty much everything in this example will be straightforward but there's just a lot of things that we have to handle individually so the first route I'll work on is to get all users route and this route will be pretty simple actually you know what I think working on the create user route will be a little better to start with so I can create a user and then I can work on the get all users route to see the user that I create it so what I need to do is I need to import a couple of things I'm going to import requests to get the request and then I'm going to import CG sana fie so I can return the information and then I also want to add a couple of imports to handle the things that I talked about earlier like the you would so I'll import you it to generate like a random public ID and then I'll also import from work Zurich security the password hashing function so work served out security import generates generate password hash and check password pass because once I put the Hat the passwords in the database I want them to be hashed so check password hash okay so now I'll go back to the create user routes and in here what I'm going to do is whenever data is passed in I'll use that data to create a user so first thing I need to do is get the import JSON data so I'll call this just data and it will be request get Jason so that's pretty self-explanatory it passes in the JSON data to that variable called data and then I want to hash a password so let's say hash password and the information that's going to be passed into the JSON object will be the name of the user in the password user so to generate the hash password I'll use the generate password hash function and I'll pass in data password because that's coming from the JSON object and I'll specify the method um let's say sha-256 and now that I have that information I can create the new user in the database so new user user and the public ID will be the string version of the you would so you would dot you it four is the one I'm going to use you can use a different one but that's the one I'm using so you at four will be the public ID and then the name is going to the data name the password is going to be hash password and finally admin is going to be set to false so actually for this first user I'll set admin to true now I've thought about again I'll set it to false and then I'll use the put route to update to an admin so I'm gonna have the new user I can then DB session add new user and then DV session commit and then what I want to do is when this gets created I want to say message new user create it so I'll save that and I should be able to test this out to see if it works so I will enter a route here so for 5,000 user and then I'll go to create so posts and in the body I will go to raw in postman and then Jason now pass in a name and a password so the name for this user will be admin and a password is going to be I should put the quotes around that password will be let's say 1 2 3 4 5 and let's put that in the string so 1 2 3 4 5 so I send this and it tells me new user has been created ok so now the next part that I want to work on is to get all users route and in the get all users route I want to return a list of all the users that are in the database so to do this the very first thing I need to do is query the users table so I'll create a variable called users now I'll do user query all so standard sequel alchemy stuff I'll create a new list to be output since I can't output the sequel alchemy query results into adjacent object directly I have to build my own object first so I'll create the object list or the output list and I'll loop over the users in the users results so for each one I'll create a new dictionary called user data and then I'll just take the values that are in the results so public ID name password and admin and I'll put them in this new dictionary that I'm creating so user data let's see public ID is going to be equal to user dot public ID so on the right hand side that's the result from the database on the left hand side is the new dictionary I'm creating and I'll do the same thing for the rest of the data so the name will be name password will be user that password and it's going to be hashed as we'll see in a moment user data admin will be equal to user admin then I need to pin this dictionary to the output list so I'll put a pinned user data and then finally I can return all this after the loop has completed so return just onif I let's say users and then users will be the output lists so now if I go back to postman and I'll go to get and I send this with no extra information I see a list of users I only have one user in the database so I have one result here I see the password is hashed and I see the public ID is a random value so now what I want to do is I want to work on the get one user route and for the get one user route what I'll do is I'll do something very similar this one will take in the user ID though which is the public ID you know I'll change this a public ID just to be a little more clear so public ID there and I'll do the same thing for bees down here so they're going to have a public ID as the parameter so now what I need to do is simply query the database for this one particular user so I'll say user query filter by public ID equals the public ID that was passed in and let's say if there is no user I'll return a JSON object saying no user found and if there is a user then it will skip over that return statement and I can build a dictionary in the same way I did above in the loop with the user data so user data it's going to be exactly the same so I'll just copy all this here make sure it's indignant properly and then I can return that in a dictionary or as a JSON object so user and then I'll pass user data okay so if I go back to postman and I take this public ID and I get first let's try get one I get no user found because there's no public idea in my database with that number but if I ask the public ID I just got from the lists then I get the information that I am interested in so pretty simple stuff so far so now what I want to do is I want to work on the promote user route and then the promote user route is going to be very similar to the get one so I'll just copy this information here because I have to query for the user first and then determine if there is a user there so I'll add that there in the put route and then once I have that I know that the user exists so user admin will be true because I'm promoting the user to admin and then DB session to commit to save it and I'll return a message saying that the user has been promoted so message the user has been promoted okay so now let's try that out if I go to put here and use the same public ideas before I see the user has been promoted now if I go back to Gibbs and I send this I see admin is now true and it's no longer false so if I go to create so posts remove the ID go to my body and create a new user I'll call this user Anthony and send and then get a list of all the users I see Anthony is false for admin but the user admin is true for admin and then they both have different public IDs and I'll create one more user just to demonstrate delete in a second so let's say delete me will be the username so send and then get everything and I see delete me is there so delete is similar to getting one user in promoting a user so I need to copy and paste that so I have the user if there's no user found it returns a message and what I need to do simply is DB session dot delete the user and then commit it so DB session that commits and I'll return a message saying that the user has been deleted so message the user has been deleted okay so simple enough save that and I'll go to my delete route but first I'll copy the public ID for it delete me and I'll go to delete and add slash the public ID I'll send it tells me the user has been deleted if I try silliness again it tells me no user found because I just deleted it from the database so that's all the functionality that I need inside of the user routes there's going to be a little more than I'll add for the authentication but for now this works pretty well so let's get started working on the actual authentication so now what I want to do is I want to create a routes called login and this route will allow me to take the username and password for a user enter it using HTTP basic authentication and then in return I'll get a token and this token will expire after some time and with that token I'll put it in a header for all the subsequent requests so I can be constantly authenticated without having to login using a username and password every single time so of course that'll make more sense once I actually demo it so let me create this quickly so I'll create the function called login and the first thing I want to do is I want to get the authorization information so request authorization and now what I want to do is I want to make sure that this authorization information is actually complete so what I'll do is I'll say if not us or not auth dot username or all not author thought passed word I want to return the make response for logging in so this basically saying if there's no authentication information at all if there's no username or there's no password return the following so I need to import make requests or make response and then down here what I'll do is I'll require that the user login so I'll say could not verify I'll pass back a 401 and I'll pass back the appropriate header so it's wwo authenticate and then the value for this header will be basic realm if I can spell that correctly and then login required will be the message that I put so that should be it and now if there's actually authentication information the next thing that I want to do is I want to get the user so user query filter by and I'm going to take the name from the office authorization so Alstott username and I'll get the first result meaning the only user because the name should be unique and of course if not user I'll return adjacent objects saying let's see chess onif I know user found and actually you know what I'll just copy and paste this because I need better login information okay so that's fine so now what I want to do is if I get passes if not user check that means that the user exists in the database so what I need to do is check the password so if check password hash I'll pass in the password that was in the database first so user dot password and then also that password is the password that was actually passed in what the request and if all this works then I can generate a token so to generate the token first I need to import JWT or jot so import JWT using pi jobs and I'll create the token so I'll create a token I'll call the resulting value token so JWT in code and what I want to encode is both the public ID so like I said I'm not going to put the regular ID in here because I don't want people to be able to guess my other IDs so instead I'll put the public ID which will be user dot public ID and that's from the database and then I'll also put an expiration in here so for the expiration I need to import date time and then I'll use date time to set the expiration date so for expiration the expiration of course has to be a typical UNIX UTC timestamp so to do that I'll call date/time date time UTC now and I'm going to add a time Delta of let's say 30 minutes and that's how long my see big time time Delta so 30 minutes will be how long the token is active it can be anything you want but I'll just set it to 30 minutes so app a config secret key will be used to encode the token and then once I have a token I can simply return it in adjacent objects or return to sana Phi token token decode utf-8 and if the password doesn't check out then I'll simply return the login require thing again so there are three cases when the login gets passed back when they don't supply any authorization information when there's no user in the database for the user they pass in or the password is incorrect so now let's try this login route so I'll go back to postman and I'll go to get and I'll copy or remove that and go to login and now for authorization I need to go to basic auth and username will be admin and the password is going to be one two three four five so update requests and I'll send and it failed so probably have a syntax error somewhere probably saved a little too early so I'll send this again and I see I have this token so with this token I will authenticate subsequent requests to all the other routes so only the login route works with HTTP HTTP basic authentication and all the other routes will work with the token so now what I want to do is I want to create the decorator to work with the token and in this case I will be using the header to actually get the token so I'll create the decorator up here and I'll put it below the models so I'll create a function I'll call it token required takes in the function that gets decorate it and I need to import wraps from func tools so from func tools import wraps so wraps and then I'll create the inner decorator function passes in the positional arguments and then the keyword arguments so first I'll create an empty token and then what I want to do is I want to check and see if there's a header called X access token so in this header that's where I'm going to pass the token if I have one so request dot headers is where all the headers are stored so if it is in headers so what I want to say is request headers X access token gets passed to this token variable that I create it and if not token meaning there is no token I'll return a message saying token is missing so message token is missing and I'll pass back 401 as well if the token is there then it will move on and what I want to do is out at a try except block because this time I'm using JSON web tokens to decode the token and if the token is invalid it will raise an exception so I need to try accept blocked so I'll have data here which will be the token data and I'll say Jason web token D code I'll take the token that was passed in and I'll pass in the secret key as well from the app and if everything works with that what I want to do is I want to query the database for the user that that public ID in the token belongs to so I'll call this current user user doc query filter by and then the public ID is going to be data and then the public IDs so data is the data in the token and there should be a public ID in there since it's unique I can get the first result if that doesn't work then I'll use the except block and I'll say token is invalid and I'll pass back 401 again so if I make it pass the except block that means that the token is valid I have a user so now that I have a user I want to actually pass that user object to the route so to do that I'll return s and I'll pass current user first and then I'll pass the other arguments so the positional arguments are the keyword arguments and finally I can return the decorated function inside of my token required decorator so now what I can do is I can ask the decorator to all the routes so token are required and once I add to this decorator I have to add current user because I'm passing it to the function i gets decorated here so the function I get decorators get all users so that's what F represents if I pass current user there then I have to accept it as a parameter there so I'll do the same thing for all the other routes that I have so current user comes first and then public ID will be one of the positional arguments and this will be exactly the same for all the other routes so current user let's see token required current user comes before public ID and let's see where is it so required current user before public ID once again and of course I have the login route but that login route is for logging in directly so I don't have to use the token required decorator with it so now that I have the token required decorator what I want to do is I want to verify that the user is actually an admin user if the yard them they can continue doing things in the route and if they're not then it will give them a message saying like hey you can't do that so I'll do that for each route and I'll demonstrate it on the get all users and then I'll just copy and paste for the rest so now that I have a current user that remember that's a sequel alchemy database object so if not current user admin meaning admin is false I want to say let's see message cannot perform that function so I'll save that and let's see what happens when I take this header or I take this and put it in the header so I'll go to get and a user the headers I'll add X access token copy and paste that and I'll send and it gives me the information that I'm looking for but now if I go back to login so so I'll go back to login and instead of admin I'll log in as Anthony which is a regular user I'll get the token for Anthony and I'll take that token and I'll put it here and I'll send and the token is invalid so I think I copy and paste it wrong I did because I didn't copy the es beginning so let's try that again okay so then and it tells me cannot perform that function and I cannot perform that function because Anthony is not an ADD and user admin is the admin user so because that's so simple it should work for the other route so I'll just copy and paste this to the other places so that should be the first thing in each one of the routes so create user as well and then promote user and finally delete user so this ensures that only admin users can do all these things so that's it for the user routes now the next thing I want to do is I want to create the routes to handle the actual to-do items to get the functionality that we're interested in in our API so the first thing I'll do is I'll create the outline of the routes so there will be five routes again so methods gets and I'll call this get all to do's and it's going to return nothing at the beginning I'll fill that in in a moment so to do and then see a to-do ID methods will be get again gets one to do and I'll pass them a to-do ID here just return nothing and then I'll have a route for creating to do so methods posts create to-do doesn't have any parameters I'll return nothing app route to do let's see methods put this will be for completing a to-do item so I need the 2d ID again let's see what should be complete to do is what I'll call it it takes in a to-do ID and then finally what I'll do is I'll ask the delete one so to do takes in a to do ID methods will be delete and let's call this delete to do so as we'll see the code in these routes will be pretty similar to the code in the users routes because there's a symmetry in the API between users and to do's even though there's different data it behaves in a very similar way so I'll return that and now I'll add the token required decorator to everything so token required and I'll meet the current user so current user goes before to D ID not after if you put it after it won't work so token required current user token required current user and finally the delete one okay so now first thing I want to work on as the create to do route and it's going to be pretty similar to how users were created but instead of a user this is going to be a to-do item so what I'll do is the first thing I need to do is get the requests Jason so get jason and in this case i don't have to check if there's an admin user or not because this is for everyone it's not for just admin users this is for like the regular users of your api so I'll get the JSON data and I'll put it in a variable called data now create a new object for the database called new to do and now instantiate it using text data text which will be one of the items in the JSON object was actually gonna be the only item in the JSON object complete will start off as false and a user ID that this to do belongs to will be the current user which is from the token dot ID and then I can add this to the session so DV session add new to do and then DV session not commit and finally I'll return a message saying something like message to do create it okay so now let's test this out I still have the token in my header so let's say to do and for the body need to change how to posts and the text will be let's say go to the store so I'd be the first to do item that I add I'll send and it says to do create it and let's add another one arm take-out-the-trash to do create it and I'll add one more let's say check the mail okay so now that I have some tutus in the database I need to be able to see them so I'll work on the routes where I get all to news first and then of course I'll work on the route where I can get individual to dues so the first thing I need to do is I need to query the database for all the to-do items that belong to the currently logged in user so I'll create a variable call to dues and I'll do to do got query not filtered by and the user ID for the to dues that I'm looking for will be the current user ID and this will be multiple so I'll use all instead of first and like the route to get all the users I'll create my own output data and I'll loop over the to do so for to do into dues C to do data I'll call this it's going to be a dictionary it's going to have an ID which will be to do ID then it will have text so to do that text and then finally it will have complete and that will be to do dot complete I'll append this to the output append to do data and now return all these in adjacent objects so to dues and output so let's go to the to do route and the authorization is not required here so I'll get everything and now I see the three to-do items that I have go to the store take out the trash and check the mail and also have three IDs one two and three and as you can see they are all false meaning they haven't been completed yet so now I'll work on the next route which is the get one to do and this once again is pretty straightforward so I'll do to do to do query filter by I'm looking for a to do ID and a user ID so this way you can't see someone else's to do ID even if you have their ID so to do ID is passed in and current user has the user ID and this should only be one so first if there's no to-do item found for that particularly D I'll return a message saying message no to do found if it is found then it will skip that and I'll just do the same thing up here so since the data is the same name I'll just copy and paste and indent this properly and I'll return a JSON object with the 2d data so to do data so let's try that out if I do to do / - and send tells me current users not defined so I use the wrong name upload current ID instead of current user so I'll save that and I'll send it again and it tells me take out the trash for number two if I go to number three it tells me check the mail and of course I have number one go to the store and remember I still have the token in the header if I didn't have that token then I get an error so now let's work on put which means complete a to-do item so these three lines will be the same because I need to find that you do in the database and verify that it belongs to the user and if there's no to do in the database then it will return nothing and just to demonstrate if I put four and send I get notes who do found for a get so with put all I have to do once I know that the to do exists in the database I'll go to the complete column and I'll set it to true and then DB session commits and then I'll say in a message to do item has been completed so let's try that out I'll go to put I'll use for tells me those who do fail but if I use number three and sin it tells me to do item has been completed so if I go to get for three I see complete is now true if I get all of them I see one and two or false but three is true because I just updated three to be complete so the last route I need to work on is the delete route so this code is going to be the same because I'm looking forward to doing the database to verify it belongs to the user and if everything is good then it allows me to move on and I'll just add DB sessions dot delete and I'll pass in the to-do item DB session commit will save that change in the database return a JSON object with a message saying to do item delete it so I'll save that and I'll go to the deletes here and I'll pass on to send tells me to do item delete it if I send that again it says no to know to do found because I just deleted it from the database so if I go to get to know to do found and if I go to the route with no number then I see that I only have 1 & 3 in the database so far so I believe that is it it's a little over 200 lines of code but as you can see everything is pretty straightforward once you know how all the pieces work individually combining them isn't that difficult so even though it took me like 45 minutes to combine everything nothing in here should have been difficult if you understand the foundation material so how to authenticate an API how to create decorators how to use sequel alchemy all that stuff comes together to build this nice restful api to generate two dews and of course if I wanted to add two dues for a different user then I change the token I login without user first I get a token back and then I use that token on the to-do routes and those to-do items will be created for that particular user because it's using a different user ID so that's it for this video if you have any questions about this video you can leave a comment down below and I will answer it if you want to get this code for the video I will put a link in the description below and you can go to that link and download the code for this video like I said early in the video check out pretty printed calm for courses after I work on this particular course I'm actually going to start outlining the next request course in Python so just watch out for that if you sign up for a free course on pretty pretty calm I'll let you know when I release any other courses and of course if you sign up for a premium course I'll let you know if I release any other courses as well so that's it for this week if you like this video please give me a thumbs up if you haven't subscribed to my channel early already please subscribe and thank you for watching this video I know it's my longest video so far so thanks for being patient and I will talk to you next time
Info
Channel: Pretty Printed
Views: 161,141
Rating: 4.9447217 out of 5
Keywords: flask restful api, flask restful api with tokens, flask result api with token authentication, flask http basic auth, flask json web tokens, flask jwt, restful api flask, restful api jwt, restful api json web tokens, flask, flask-sqlalchemy, json web tokens, jwt, flask-sqlalchemy api
Id: WxGBoY5iNXY
Channel Id: undefined
Length: 48min 37sec (2917 seconds)
Published: Fri Jun 23 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.