React Typescript & Go w/ Gin Backend JWT Auth Part 1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody welcome to part one of creating this simple authentication app using typescript react for the front end and go for the back end using the gin framework so in this part i'll be creating the backend using the gene framework just as i said and create endpoints for api register login getting a user and log out i'll be using mysql for database and incorporating json web token for authentication and out of middleware to resolve course policy error so in case you don't know what course is it stands for cross-origin resource sharing and i'll be going over that later in this video but if you're interested you can pause the video and watch this real quick i mean read that real quick so let's get started i always want to get started with creating a repo in github i'm going to name my repo go slash react auth demo i'm going to make it public and not a re not add a readme file or get ignore and just create repo and it's going to tell you how you can initialize your git in their local project so i'm going to come back out here and if it's your first time creating a go project once you install go you're going to realize how you have a go path configured for you so that's going to be inside your home directory and you would want to create directories like this source github.com and put your username there and dashp is going to create all the folders leading up to the last one here so once you create that you're gonna cd into that enter and here i want to create this directory that matches the name of your repo so make the go react auth demo cd into that and then open that up in visual studio code so if you don't have that shortcut to open the visual studio code from the terminal just do command shift p from your visual studio code and look for a shell command install code command and path and just enter and that shortcut is going to work for you so here i will do command j to open up the terminal so the first thing i want to do is to initialize git so i'm going to copy this first line and paste it here this is going to create a readme file in my project and just copy line by line to do git init and then i'm going to create my first commit actually i want to add this readme file stage it and then make my first commit and say it's going to be my first commit and then make main as my main branch you come back here and then add the link to this remote repository we created and lastly copy this last line to push our change to the remote repository so if you come back here and refresh you're going to realize how the readme file has been pushed so now that we have git initialized i want to create a folder called backend and create my main.go file in there and main.go is always the first file that we want to create because this is the executable file where everything will be executed so here i'm going to name my package package main and that's by default and create a function called main and here i'm going to call a function start application from the app package which i'll be creating in the backend folder so inside back in folder create app slash application dot go and that's going to create app folder which is our package and then create the file within it i always want to specify my package and then create my function called start application in here i want to define my router and i want to create my router using the gen framework so simply just google gen framework or something and then come to this repo and it's going to tell you how you can install so copy that first line there copy this one come back and just paste it here and that's gonna do its thing while it's doing its thing i'm gonna create a variable called router and i can do that by doing gen.default and everything is inside this documentation if you want to refer to anything so do gen.default and on this router we can call our request get our post and then do a run on the port that we specify so back in application.go i'm going to do router.run and say i want to run this on port 8081 so i'm going to save this and this is going to automatically import the jing from github and then if i save this it's going to call the app package that we just created there in here i also want to call map urls which i'm going to be creating within the same package app so here i want to create router.go and as usual name my package and create this funk map urls save this real quick come back to application.go and save it and since this function is created within the same package app you don't have to worry about the scope in other words we can access router that we created here in application.go and create post and specify the endpoint that we will be creating and here i wanna call users.register and this is gonna be our controller and this is the register function that we will be creating in the users controller so inside the backend folder i'm going to create controller slash users slash user controller dot go again the name of the package is going to be users because that's the parent folder of this file in here i want to create a function called register and this is where we would be passing in the gene context so if you look at the documentation this is the part we are creating in the controller and this receives a context and we can send a response back using the context.json if i save it it's going to auto import the gin framework the first thing we want to do is create a variable called user of type user struct which we have to create again in the backend folder i'm going to create domain slash users slash users underscore dto dot go so data transfer object is what we are going to be using to pass data back and forth within the project and this is where our user struct is going to be defined in so again i'm going to name my package package users and create this struct called user here i'm going to have id of type in 64. first name of type string last name of type string password type string and email type string and i also want to specify how each of these fields are going to be recognized in the json format and this is going to help you later when you are interacting with the database so i'm just going to do it right now while i'm making this so only a small difference json it's going to be the same for the password json collin and just say email save it now back in the controller if i save it it's going to auto import the users package from the domain folder and the first thing we want to do is see that it should bind json and pass a pointer to this user so what should bind json do is pass the data from the request body in the context and store this in a pointer to the user and if you hover over it you see how this returns an error so i'm gonna do air like this and check if there is any error and if you're new to go this first statement is going to run first and return an error if there is one and we're doing an if statement on the second part here based on the error that was returned and if there is an error i want to do c dot json and pass an error but you're going to realize how json takes in a code and an interface so you want to create this rest error struct that will be used across the project and it will make sense once i create it and show it to you so in the backend folder i'm going to create utils folder inside errors folder and create rest underscore error dot go specify my package name and here i want to create a struct call rest air and this is going to have a message of type string status type int and an error like this and we're going to have two different types of errors that we want to handle for now and of course we can create more along the way and this returns a pointer to the rest error struct and just return rest error like this and the message is going to be the message that's passed in the parameter status is going to be http status internal server error and error is going to be internal server error and you need a comma here in the last field similarly i'm going to create new bad request error and pass a message of type string and this is going to return the pointer to the rest error that we created above so this is very similar to the one we just created above here i want to pass the message the status is going to be http dot status bad quest in the error it's gonna be bad underscore quest comma save it so we now have the errors package that we can call from anywhere in the project so back in the controller i'm going to delete this for now i'm going to call this package and do errors dot new dad request error and this automatically imports the errors package and here i'm just going to say it's an invalid json body and save this in a variable called error and now i can do error dot status and then pass in the error as an interface and do a return here so that we exit if there is an error so having this errors package make handling errors easier for us and we're going to be using this across the project again and again so if we don't have any error i want to call create user method from the services and pass in is user so services is where we want to handle our business logics and just like how we did before i'm going to create services and create user service dot go in here package services and create a function called create user and i'm going to be passing in the user struct and it's going to return pointer to the user and pointer to the rest air like this so save that okay i'm going to come back to that later the first thing i want to do is validate this user and i don't have this validate function yet and that's something i want to create in the data transfer object so i'm going to create this function validate and this is going to return a pointer to the rest error and unlike the functions we created before i want to specify a receiver for this function and by specifying a receiver this becomes a method and not a function in here i want to do strings dot trim space and pass in the first name of the user and save it in the user.first name save it same for the user that last name i want to do trim space space and pass in user dot last name user.email and do trim space pass in user dot email and if the email is an empty string i'm going to do bad request error and say it's an invalid email address so i'm just keeping the validation simple for now and you can modify to meet your needs so user password like this and the user password is an empty string i'm going to return bad request error and just say it's an invalid password and if everything goes well i'm going to return hill so save it back into services validate is going to return an error if there is one and i want to handle that error like this if there is i'm just going to do nil air like this in the next one we want to do is actually encrypt the password before we save that into the database and i'm gonna be doing bcrypt to encrypt the password so simply do bcrypt enter and that's gonna auto import decrypt and do generate from password so generate from password takes a byte slice so i'm going to parse user password into a slice of byte and for cost i'm going to keep it at 14. and store this in a password slice and an error and i'm gonna check if there is any error and return errors dot new bad request error and just say failed to encrypt the password or something like that and since the user that password is a string i want to actually parse it back to the string and you can do it this way and now that we have saved the encrypted password i want to do user dot save and this save method is something you want to create in the data access object and we will create data access object in the same package we created the data transfer object so here simply create users underscore data access object dot go and data access object is where we interact with the database and this is where all our queries would be in so again i'm going to name my package and create this function called save and this is going to return a pointer to the rest error like this and before i continue i actually want to set up my connection to the database so in here i'm going to create a data source folder my sql folder and create users db and create usersdb.go inside it here i'm going to name my package users underscore db and create a function init like this we're using mysql driver for our project so simply just look for go sql driver slash mysql and just copy that first line here and paste it here to install it okay and here i want to import do underscore here and type that go sql driver mysql like this you need to have this underscore to ensure that this import stays in this file in here i want to create a variable called client and this is going to be a pointer to the sql.db and in here i'm going to do sql.open and name it mysql and pass the data source name in here which we have to create so i'm going to create this variable called data source name and this follows a specific format where it has a username password at tcp and you pass in your host slash and user schema so this is the format that the data source name follows so i'm going to create fmt s print f and insert placeholders for the username password host and user schema actually i'm going to create those variables up here first so that it makes more sense to you for me the username is root and i don't have the password set and the host is gonna be localhost 3306 and schema that i created is users db 02 and for your information i'm using mysql workbench to interact with the database so you if you have never used mysql before you can just install this mysql workbench and create a connection specify your hostname and the port your username and set the password to something and once you have the connection you will have access here and you can create a schema by creating this button right here i'm going to zoom in for you so create a new schema and the name of the schema that i created is users db underscore zero two inside i created a table called users and these are the five different fields that i created inside the users table and i also have some mock data in there and it's up to you to create those so back in the usersdb of course this is not the proper way to store your username and password you would want to store these credentials in a env file and specify that.env file in your git ignore so that it doesn't get pushed to the remote repository so i'm going to continue off with this data source name so i have these four different placeholder for the username password host and schema so i'm going to just do add tcp pass in this placeholder and like this and lastly i want to do charset equals utf 8 and then i can do password host schema so with this formatting we would be passing these four values into these four placeholders that we created here and now before i continue i want to create this variable of type error and sql.open is going to return a client and an error save it and the reason i want to create this variable error like this is because i already have this client defined up here and i can't do dynamic assignment here because i already have a client defined so i also wanted to define error and get rid of this dynamic assignment so i'm gonna handle the air real quick and do panic air if there is any and if there isn't any error i'm gonna do client.ping and what dot ping do is just verify as the connection is made and this is going to return an error if there is one and i'm going to handle the error the same way and return panic and lastly i'm going to do lock.printline and say database uh successfully couldn't configure it or something like that so save it and now we should have the database configured for us so now that we have the database configured i'm going to come back to the data access object in here i'm going to call that client variable that we created in usersdb and i'm going to call prepare method and pass a query insert user into the method which we have to create so variable appear and query insert user and this is going to be insert into users table specify the column names email password where we would be passing in these four different values so i'm going to save this real quick and repair returns a pointer to a statement and an error and this creates a prepared statement for later queries or executions so here i'm going to do statement error and handle that error and return errors dot new bad request error and simply say database error for now and we always have to close the statement so before i continue i actually want to specify the receiver of this method and it's going to be a pointer to the user and once we have the statement without an error i'm going to do statement.execute and pass in the first name last name email and password so these four values come from this pointer to the user and this pointer to the user comes from the service here and this is actually coming from the controller itself and this pointer to the user stores the data that we have received from the request body so by creating the user go to service and we want to save that and we can access the data from the pointer to the user and we are inserting those data into this statement so by doing statement.execute we're passing these four different values into this query that we created and this would be passed into the database with the values inserted into these question marks and this is going to return the data back to us from the database so execute returns result and an error so i'm going to call this insert result and okay save error like this so handle that save error as usual and return errors dot new internal server error and just say database here actually i want to call this new internal server error as well actually get rid of this bracket right here the last thing we want to do is insert result dot last insert id and last insert id returns an integer and an error which is actually our user id and an error so the reason we want to do this is because the id is auto-generated in a database when we pass in these values and we actually want to call that id that was auto-generated from the result extracted store it in user id and then store that manually in our user id before that we just want to handle the error like this return errors dot new internal server error and just say there is a database error and if we reach this point i'm just gonna return hill save it now that the data access object is created i'm gonna come back up to the service and user.save returns and air if there is one and i'm going to handle that air and return mil in the air so if we reach the end without any air i want to return a pointer to the user in nil like this and save it move back up to the controller create user is gonna return a result and an error and i want to do save error not equals nil and in the controller i want to see that json and call the status pass save error if there is one and do a return and lastly if we don't have any air i want to send status ok as a response and pass the result back to the user and now that the controller function is created i can save this user style register it's going to auto import and that's pretty much it for the register endpoint so now that we have the database data access object and everything in place everything else is going to be much easier just that we have to implement json web token and everything like that so let me actually just create all the endpoints right now log in here i'm going to do users log in and this is going to be api user where we be getting the user and passing the data to the home page so we want to get the user information and display it in our home page the last one is going to be log out and this is going to be api slash log out and pass in the log out function from the user's controller and i'm not going to save it for now until i create those functions in the controller so i'm going to start from the login function and inside the controller we always want to pass in the gen context as a parameter and just like before i'm going to create this user struct variable user and do c that should bind json and pass in the pointer to this user struct and this returns an error if there is one and handle that error inside this i'm going to create a variable error where i do errors.new bad request error and say it's an invalid json and i can do c.json status and pass the error and do a return and if we don't have any error i can call get user from the service and pass in this user i'm not going to save it i'm just going to move to the service and in our service i want to create a function called get user and pass in this user struct and this is going to return a pointer to the user and the rest here like this so in our login function of the controller we are using our email to login so i first want to create this variable called result where it's a pointer to the user storing the value of the email from the user that's passed in so pass in this email into the email field and i'll do result dot get here so this get function is going to come from the data access object in here i first want to create my query so just like i said we are getting user by email so we are doing select id first name last name email and password from users where email is unknown so down here i'm gonna create this get by email actually let's rename this to get by email just because we're getting by id later on we want to distinguish the two and this is going to return a pointer to the rest error and here i want to specify that this is a method on the pointer to the user so just like how we did above um we're going to call the db.client and do prepare and pass in that query that we created above and this is going to return a statement in your error if there is any error i'm going to do errors.new internal server error and just say it's an invalid email and i'm going to close that statement like this and unlike how we did statement.execute above when we created user i'm going to do a query row and pass in the email query row returns i mean it executes a prepared statement and returns a pointer to the row so this is going to return a pointer to the row which i'm going to store in the result variable and i'm going to do result dot scan pass in a pointer to the user id a pointer to the first name last name email and password like this so scan copies the columns from the matched row into the values pointed at by test so in other words this statement.queryrow is going to return the row that we want from the database and by doing a scan on the result we are passing the data from the result into each of these fields based on the matching columns so it copies the columns from the matched row so you're going to have the data stored in the pointer to the user and this is going to return an error if there is one and you get error they'll like this return error new internal server error and just say there is a database error actually this has to be id with capital d and lastly i'm going to return mil save it so now that we have the data access object in place for getting the user by email so this is going to return an error if there is one and i'm going to handle that error as usual so by doing get by email on this result if we don't have any error we're gonna have access to the data in this variable like the email first name id last name password etc so since our password is encrypted using bcrypt we have to decrypt it back we can decrypt a password by doing bcrypt dot compare hash and password so compare hash and password takes in the hash password from the database and the password that we have from the request body so result is what we have from the database and this user is what we have from the request body which is from the user and since this only receives a slice of byte i'm going to convert i'm gonna parse the result password into a slice of byte and same for the user that password like this and this is going to return an error if there is one and i'm going to do return nil errors dot new bad request error and just say failed to decrypt the password or something and when we are returning the result back to the user we don't want to include the password in there so i'm going to create this variable and say it's without a password of course you can name it however you want and in here i'm going to create the fields pass in result id first name is gonna be result dot first name last name is gonna be result that last name email is going to be result.email actually this is users.user and i'm going to return results without password as a response now that the service is in place we can go back out to the controller this returns a result and an error if there is any error i can do see that json and send this back to the user as a response and do a return so here comes one of the most important part of authentication where we would be creating jwt claims and token so in order to create json web token in our project i'm going to be using this jwt dash go and i'm going to put that link in the description box for your reference so simply include that in the import here of course get rid of this https here i first want to create a claims variable and i can do that by doing jwt dot new with claims and this returns a jwt dot signing method which is gonna be jwt dot signing method um yeah let me just type it out signing method hs 256. and then it takes in jwt dot claims and it's going to be jwt that sender claims and inside the standard claims i'm going to pass in issuer which is going to be a result id that we have but result.id is of type integer 64. so i first want to parse it into int and then do string convert dot i t o a and wrap the entire thing up to convert it into a string and then also specify when it's going to expire and i'm just going to do time dot now and add time dot hour times 72 dot unix something like that save it and claims is not used which is why i still have that red line and then i can do claims dot signed string and what sign string do is actually get the complete signed token using the secret key that we're passing in so in here i want to pass in our secret key which can be anything but i actually want to specify my secret key up here so create a const secret key and i'm just gonna do a random string as my secret key so here i'm gonna pass the secret key in here as a slice of byte so this is going to return our token and an error and i'm going to check if there is any air i can do you know i'm going to create a variable called error if there is any error and just say errors dot new internal server error and say something like login failed and inside our c dot json reduce error that status and pass in the error and do a return and now that we have the token i can create a cookie by doing c.set cookie in the context and passing the name i'm going to name this jwt the value would be the token itself the max age i'm going to keep it at 3600 and for path i'm going to do slash localhost false or secure and true for http only now that we have set the cookie i can send the response back by doing status ok and putting the result there save it so the login function is ready and now this endpoint should work properly and the next thing we want to do is getting the user information from the database using the id in the session cookie down here i'm gonna create a function called get and pass in the gen context as usual and here i want to retrieve the cookie with its name jwt and this is going to return cookie and an error if there is one of course handle that error as usual there is that new internal server error and just say you cannot retrieve cookie or something and do see that json get error.status get error into a return and if we don't have any error i want to do jwt.parse with claims and this takes a token string which is our cookie and as its claims i'm going to pass in a pointer to the standard claims and lastly it accepts a key func so if we actually go into parts with claims and dive into the key func it's an interface that accepts a pointer to a token and returns an interface and an error so i just copied that i'm simply gonna paste it here and we actually have to specify that this token is from jwt like this and this key funk is going to return our secret key in a slice of byte form secret key and nil like this and this returns our token and error and i'm gonna handle that error for the tenth time so here i'm going to do errors that you in turn on server error say error parsing cookie and do c dot json rest error status and pass in the rest error and do return so if we don't have any error retrieving the token i want to do token dot claims and store that claims in the variable that we created but what we actually want to access is the issuer in the claims but you're going to realize how we cannot access issuer from the claims so if you look into the claims you realize it's just an interface and if you scroll down just a little bit we realize how the issuer actually belongs to the standard claims struct and not the claims interface so back here i want to parse this into a standard claims a pointer to the standard claims by simply doing a dot and passing that in like that and now our claims variable is going to be of type standard claims and now i can access claims dot issuer like this and i actually want to parse this into an integer of type 64. so i'm going to do parse int of base 10 times 64. i'm going to save this in an issuer like this so handle the error again press error errors new that request error user id should be a number see that json rest pair that status and rest air and do return so if you recall the issuer has our result id stored in it which is why we want to access the issuer like that so issuer is basically the id of our user and i want to do services get user by id and pass in this issuer as its parameter so inside the service scroll down and create a function get user by id and pass in user id of type integer 64. this is going to return a pointer to a user and a pointer to the rest error here i'm going to create a variable called result and this is going to be a pointer to a user that stores the user id in its id field and i'm going to do result get by id so this is something we would create in the data access object so inside data access object i would start with query get user by id and this is going to be very similar to get user by email just that we wouldn't be retrieving the password and this would be id instead of email so scroll back down and here i want to create get by id and this is going to return a pointer to the rest error you should be used to it by now and it's going to have a receiver where it's a pointer to the user and again it's gonna be client that prepare and pass in that query that we just created and it's gonna return a statement in error and we're gonna check if there is any error new internal server error and just say there's a database error before statement.close and if we don't have an error we're going to do statement dot query row and pass in the user id that we have and this is going to return a pointer to a row just like what we had above and then i'm gonna do result.scan and pass in user id user first name user last name user.email and that's actually it and this is going to return an error if there is one return okay and return errors that you internal server error and just say there is a database error if there is any return nil if there isn't so now that we have this in place let me move back up to the service and this is going to return there and check the air okay return they'll err like this return result the nil save it then move back up to the controller get user by id is gonna return and point it to a user and rest pair so i'm gonna say result and rest error uh if rest error is there i'm gonna actually see that json rest error dot status and pass the rest there do a return and if we don't have any error i'm going to do c dot json status okay and pass in the result as a response save it and that's it for getting the user with id we have one last endpoint that we have to handle and that's going to be the logout and the logout is going to be the simplest of all so in the controller i'm going to create a function log out and pass in the gin context as usual and in order to get rid of the cookie we actually have to override that cookie using the same name not passing anything we want to set the max h to a negative value set path to empty string set domain to empty string and just say false and true for these and do c dot json http status okay and pass in a body with a message saying success and that will get rid of the cookie that exists in the session and that's pretty much it for logging out and it seems like we're done with these four endpoints but there is one thing that i pointed out in the beginning of this lecture which is adding a middleware to resolve course policy error so course stands for cross-origin resource sharing and browsers restrict cross-origin requests for security reasons and making a request to a different origin from front-end which is our port 3000 would invoke this course policy error so since our backend is running on port 8081 when we're trying to access the backend from the front end we're going to get this kind of error saying this has been blocked by course policy so we would have to set the appropriate course headers in the request and we can use a package called jin contrib slash course of course i'm gonna put this in the description box as well and you can simply copy the first line here to install it paste it enter and if you scroll down it's going to show you how you can implement this so just copy this line here and we're just going to make small changes to it okay i'm going to save this it's going to auto import in here i'm going to say http localhost 3000 and instead of put in patch i'm going to say get and post and for allowing headers we're going to do content dash type and here as well i'm going to change this to localhost 3 000 like that so that's actually it for our back end we now want to test them out so i'm going to clear this real quick cd into our back end and go run main.go so our endpoints are running on port 8081 i'll be using postman to test these endpoints so now that we have everything in place let's test it out so i'm going to be passing in this first name last name email and password to api register send it and i'm going to have the password encrypted and get this data back in the response and now if i were to copy the same information and pass it into api slash login just pass in the email and password like this since the login was successful it's going to return that same result in the response and if you log in you realize how you have the cookie created for you of name jwt here so if we actually try to get the user we're going to get the user except for the password and we still have the cookie stored and once we log out it's going to display the message success and we're not going to have the cookie anymore so that's pretty much it for the back end part of this authentication app in the following part i'll be creating the front end part of it using the typescript react i hope you guys found this helpful thank you all for watching
Info
Channel: Beaucoup Coding
Views: 1,498
Rating: undefined out of 5
Keywords:
Id: QD2BCikYCyc
Channel Id: undefined
Length: 63min 8sec (3788 seconds)
Published: Sun Mar 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.