Authenticating your APIs in Golang (HTMX project)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so when it comes to authenticating your services and apis there are a couple of ways of securing them but in this video we're going to implement a l to our application where we'll be able to authenticate the user from any existing provider Google GitHub Discord whatever really so we are following the previous video where we have built a full stack application with go and HDMX but it was missing authentication so what use really is an application with authentication right so on this video we are going to follow from that work that we have already done and just focus on securing our API now you don't really need any background on the projects itself to learn and profit from this video because I've made this specifically so you can get the value without watching the previous video but if you interested in developing with HDMX and go I'll leave the video in the description below so before before we start I just want to show you this little diagram from White B go basically it explain some of the most used artification methods so here we have the Bas basic artification token artification for example the J very used um then we have Oath which is what we're going to be using in theory what's going to happen is that we are going to communicate with an external service that's follow the a protocol and then we have API codification which is pretty simple as well uh but this is just an overview so the reason that I have decided to go with o is because it's easier to set up we don't have to also implement the authentication nor manage it and we are relying on establishing solutions that are secure now for the project stack this is what we're going to be using um this is what we have already built on the previous video as well so it's a god stack goang Temple and HDMX and we're going to use the package called go as well so it's going to be a lot of goth in this video so this is the package that we are going to be using uh it's a very solid solution to implement authentication against a myriad of providers so here we have all of the providers so basically whatever youone implements you can Implement against it um it's pretty solid so let me just show you the application that we're going to be building the is nothing nothing special just because I want to focus on the features itself so let me just clear here and you notice that I got redirected to the authentication so I cannot access this application without authentication so I have here implemented two providers Discord and GitHub and let's click on the Discord so I get redirected to the Discord notification I am authenticated in Discord so everything works and here I have on the nov bar my username from Discord my avatar and all of that information and I can see the application by the way talking about Discord I'll leave in the description below below the Discord Community if you want to join where you can post anything you want questions and join a community of like-minded people I'll will that in the description so here I have the C available cars if I click on it I can see the whole application and I can do any functionality so every end point here is authenticated uh if I start searching for Matata I can see and I get the results if I'm not authenticated uh for example let me clear the cookies I am going to have a problem so I am redirected to log in where I need to log in all right guys so let's then get started this is the B PL code that I'm starting from if you're wondering this is the the repository that we're starting from exact same so we want follow along uh do a um a clone of that repository and then let's get started and so the only thing that I have added here is that I have added this section here with these four end points that we are going to populate currently they have nil handlers and we are going to implement them so the first one is the o/ provider where we can pass in any provider and we're going to start the authentication process then we have the Callback which is what the oo is going to call so the the provider for example the Discord provider is going to call this end points and then we are going to have some implementation in there also we have the log out of course for any provider so we can make sure that we are logging out of the session on the providers and finally we have here the view for the login where we can actually have a selection of the providers where you can log in so before starting let's install the onway package that we need for this project which is the goth from Mark Bates let's go to the console and paste it in and install it after that let's start by populating this Handler so we can have an idea of what is going to happen let's create here a new file called o.o from the package handlers and let's start by creating the login view so I think it's fair to start from there so let's call this handle and it's going to take in a response writer and a request and if you don't recall the Handler basically it's a service that receives a dependency which is the storage and the storage is what it communicates with the database take for example a repository so here on the login what we do and the only thing we do is we go to the Views and return the login View and render it uh with HDMX so let's do a render passing the request context and the writer and this is it so whenever we hit slash login we should go to the login the hand login so this is that login screen so this login view is pretty simple so if you watch the previous video This is how we are building DMI with Tempo and HDMX we are wrapping this view with the page and here we pass in false so we don't have the navigation bar and what we do is just R render two buttons one for the Discord and one to the GitHub where we're going to call the SL o Discord and GitHub provider which is this endpoint here so let's run the project and see if that works so I'm going to hit a to start in the live watching it's started correctly let's then run the make Temple which is going to start watching for temple changes and then let's also create the Tailwind uh Watcher let's go back to the browser and here we have our application running let's hit the slash login and we should have our UI now this is not going to do anything right now if you click we get to 404 because we need to implement it ourselves first so let's go ahead and implement the Handler for the h provider so what start the authentication process let's go ahead into the same file and create the handle provider login and it's going to receive again the jtp handle Funk so the first thing we do on this function is that we're going to check if the user is already logged in if it is we can return to the homepage for example as we start the auth app ation process so to do that we can call the gothic do complete all function so this does what it says it returns a user and an error and if the error is equal to nil it means that the user is authenticated and we can basically just log it so we can have some visibility else we can go to views. home. render and we pass in the context again else it's because the authentication process is not at done so we need to start it so Gothic do begin off Handler and this is basically what the handle provider login does so let's hook this up to the endpoint on the main handler. begin provider login so this starts but first we need to set up the authentication Service and then we need to also set up the Callback so we can have some functionality so let's just start by doing the Callback and then it's going to make sense in a bit when we start the the the service uh with the providers right so the Callback function is going to have the same functionality here has the uh the complete user OD so we can just copy this line here and paste it here and basically we want the user and the error then we check if the error is different than nil and here we can do um basically you can just print to the to the browser and N we can handle this better but just for teaching purposes I'm going to keep this simple after that we just said in the Heather basically we just redirecting the user to the um slash which is going to be the homepage so it's going to be location and the location is going to be the slash then we also write in the and the header HTTP do redirect temporary which is this one now we're not going to do anything with the user right now so let me just print here the user I'm just going to print the user first name for example if you want to check what properties you have you can just go inside it uh for example cop with O and here it Returns the go. US and then let me hook up here on the main the cback function so with that we can actually start implementing and initializing The Goth back package with our providers so let's go ahead and create here a new folder called services and once create a new folder again called o and I'm going to call it o.o it's going to be from the package of and here we're going to have a service called authentication Service it's going to be just a Str with some methods in it but now it's going to be empty and let's create the Constructor as well so we can start it in the service in a good way it's going to return a pointer to off service then we go to goth do use providers and here we can list any providers that we want from the GitHub so if you go here into the documentation you can see all of these providers and you can go inside examples main. go and here you have a list of all the providers and how we can Implement all of them I'm going to go with um Discord for this example but uh you can go with any provider that you want so for Discord I'm going to do discord. new now this method here receives three key ingredients so it's the client key the secret and the Callback URL so this is how you authenticate against your Discord implementation so for example if you want to do Discord what you can do is go to Discord I'm going to leave this link in the description or you can go to Google and type in uh Discord oo and you're going to be redirected to this page or GitHub o or Google oath every provider is going to have a different way of creating the credentials uh for example let's click on register in new application let's create a new application going to call this example so this is how you do it for uh Discord for any provider is going to be pretty similar to this so here I have my client ID and the secrets which you need to copy and paste B them here on the new uh but the correct way of doing this is of course putting them into environment variable because these are sensitive informations that you don't want to share so here on this project we already have a way to communicate with environment variables on the config what you do here is that um we have this Singleton here this Global variable that we can access the environment variables of the application whenever we want so I'm going to call this add Discord client ID it's going to be a string and then we also have the Discord client client Secrets then here we need to populate on the inth so it's going to be Discord client ID get M I'm going to call this Discord client ID and the default value is going to be empty and the secret as well so the way that I'm handling enironment variables on this project is that I am injecting them into runtime for that I'm using a tool called deer M this is what I usually use on my videos you can inject perment variables on the Fly and this is how you can do it on the cloud as well so it's always ready to be used on the cloud um here I have my M RC file with this environment variables already if you don't want to use de M you can always use uh go. M which is a pretty popular solution as well where you can just create yourm file and load them from there all you need to do is do changes here enter load them correctly uh how he wants right so here on the authentication Service now that we have the environment variables correct the setup let me just paste in those values from the config this is how you access them and here is the Callback URL which is going to be um actually let's create a function for this let's call build callback URL because it's going to be pretty similar to all the providers and this provider is the Discord let me create this function here let me just actually return the off service and here the build ra it's going to receive a provider and it's going to return a string as well and this function basically what it does is that it's going to return a URL in the form of the uh public host the port SLO the provider SL callback so this is going to be the URL that we have here on the uh the main file this is the one and then we are getting these variables from the environment variables as well so the public host the port so these are the default values the HTTP Local Host 80 part this is what we are running our application so if you wanted to add a new provider you can just do discord. new and do the same exact thing um Discord uh GitHub for example you can use Google and the process is going to be the same the only thing is that you need to add a new enironment variables and you can get them from the provider uh documentation so let's go back to the main.go and get a hold of the authentication Service so it's going to be inside the O new o service and let's send this to the to the Handler so the Handler is going to receive new dependency the O service I me just require it here uh it's going to be called just o because we're going to access this a couple of times or do service it's going to be received here in a Constructor as well and just like so so let me just save this and I think it is working before we can test we need to configure on the o providers the Callback URL so foras is going to be the Local Host 8080 so if you are running on a different host or Port don't forget to adjust that it's going to be the Discord slack This Is the End point that we have set up on the main let's just save uh but before that we also need to choose what informations we want to get from the user so for example I could get the identity and the email why not and then we need to select the redirect URL which is the one that we have just created so let's just save so let me just restart the server I go back to the application refresh it and you might be wondering why we don't have why we are not redirected automatically to the login page that is because we need to implement that functionality of course um what we going to do in a bit is that we need some sort of middleware or a preh Handler for all of these handlers that we want to authenticate and then if the user does not have a session for example in the cookies we are going to redirect back to the login page else we just redirect to the Handler which renders the page but first I want to test to see if the authentication is working then we can worry about that so if you click on Discord you should be redirected to the Discord login page it's going to authenticate automatically because I already have an active session on Discord and we should be redirected back to our application so everything should be working but let's check the logs and see if anything is out of the ordinary so here I have the username log that we got from the Callback so we are sure that is working because we are getting the user name information from Discord um and you might see some of these logs and this is very important because no session invironment is set and what that means so this is coming from goth what that means is that we need some sort of a storage session so we can have our user logged in into the server so to fix that let's go back to the service of.go and here on the Constructor we need to set in the gothic do store and this needs to be set to an interface so let me just do nil and import Gothic so this needs to be of the sessions. store implementation so it needs to implement the gets the new and the save this is how we're going to store the user session but Gothic does not handle any of that it just requires an implementation so we need to pass one implementation and the implementation that I have decided to go with is the gorilla sessions package so we could build ourselves that it's somewhat simple but for learning purposes and I really am a big fan of the goral packages um this is one that I have used multiple times and this is basically what um what we're going to do when we create a session so we can fetch a session we can then set values and save it now to do that let's do uh the pendy injection of course we're not just going to uh start here an implementation so let's do again a parameter as we should so we go to from sessions do store so this is already from the gorilla sessions so make sure that we have that imported on the package and install it so go get uh with this link then we just hook in the store here like so so this should not be a pointer here now on the main file we have this error where we require a store so let me just um do a session store and we need to create some sort of session store somehow so let's go here to the services of let's create a new file called sessions. go so this way we are de coping the session storage from the authentication services so the way that we're going to hold the session there are a couple of ways but I think the standard way and one which is very easy to implement which is cookies so whenever we get the response from Discord they are going to send us a token and we can store that token into the session browser of the user and then we can validate that cookie against our authentication to see the user has a session so here I'm going to create the new cookie store implementation and it's going to return as sessions do cookie store and inside here let's do a store and initialize the gorilla sessions sessions. new cookie store like so here is going to receive a key a key PA can pass in multiple or just one uh let me just continue with the pH I'm going to explain what this is so the store needs to have a Max H for the cookies which is going to be again a value um you can actually just start here with a configuration I'm going to call this session options and this session options are going to be the cookie key cookies key all of this is coming coming from the secrets this is something that we should hold there as well the max H it's going to be an integer we're going to have another one it's going to be the HTTP only and we also have the secure I don't want to hardcore any of these so here I have added some description uh for these two properties because they must be set to true if you are applying this to production and serving this over https which you should now then we're going to receive these properties from the arguments so this is the implementation for the cookie store I just set these properties from the options pretty simple and then at the end I return the star here we can now get a hold of O new cookie store and we pass in the options which we can actually just initialize them from here and again this is how I'm going to get access to them so I'm go to the config do Ms and access these properties that we just need to go ahead and create them so here on the config let's go ahead and populate this I have added these four new properties and now we need to initialize them here on the in in it config as well like so so here I am declaring a new two new functions which is get M has an integer I am handling my implementation for getting Ms so let's just add this two implementation real quick because this one get the m has a string because the Val here is a string but we need to have uh these values has in es for the implementation so here are the implementations pretty straightforward I'm just going to skip over them basically you do the same thing which is the l cap F but then we convert this to um an integer and here the booing as well we PR has a booing and here we have the the the secrets so here on the cookies off make sure that you are overriding this Val here with the secure one because I'm just using this default one um add this one in the environment variables as well then here on the cookies of agent SE seconds basically we want to set the the duration of the session cookie in seconds so for example let's do this for 2 days so it's going to be 60 * 60 * 24 then you choose the number of days that you want so it's going to be one day two days uh for me it's going to be uh two days but I'm going to move this out I'm going to start this into constants I think it's easier to read so two days in seconds then here on the top I'm going to have this constant here two days in seconds like so then I had just forgotten to add the cookies key here on the new cookie store then here on the main this should not throw any errors you should have everything configured here we are passing in the session store inside the new o service setting up the store and now we should have a storage a cookie storage working let's try out so let's go ahead and hit the SL L and do the whole process again so it worked let's go back to the logs and see what happening so here we have the username and the other warnings have disappeared so that is good progress and if you check in the cookies we still don't have any cookies seted and that is because we need to manually set the cookies of the user session and that is what we are going to do so here on the cback provider let's go to the uh to the method and here where we log we should actually set the user session as well so uh we're going to have some sort of function here on the o that is going to set that session uh so let's go here on the user service o and let's create that function so I have called this method called store user session it's part of the O service and it receives the uh the response the HTTP Handler and then the you goth do user that we have here on the um the response from the authentication then we're going to basically go ahead into the gorilla sessions and use this snippet that we have already um seen we're going to do some adjustments let me copy that and paste it here instead of being store we going to have access to the gothic do store and we can get the session by session name I'm going to start this into a variable I think it's easier so session name I'm going to put it here and it's going to be called for example user session this is going to be the name of the cookie like so and here we can just paste in going to remove the comments we don't need them right now and I'm going to return nror like so and at the end I'm just going to return nil now the value that we want to store these of course are just for example purposes but we want to store in the user which is going to be equal to the user that we received on the parameter so this is how we store the user session into the cookies now let's consume this inside the um the handle call back let's get access to the error off service dot store user session here we pass in the Handler and the user and we just handle the error if error is different than nil we print it or something else and then we save this file and we should have the session started so let's try again that let's go here to the C show login and now let's take a look here at the cookies and here we have the user session started in so we can have here the name and if you take a look at the time this is the time that we have um seted into the max age time so it's going to be into two days so there are just two things missing which is having here on the D Bar the user the the logged in user information and then having um our end points secured because even if I'm not logged in if I clear my cookies I can still make requests here on the application which is not good uh that is the reason why we are adding authentication in the first place so here on the authentication Service let's create a new method called require o and required o is going to receive two parameters the first one is going to be an HTTP hand or fank so the endpoint Handler and then the store for the odd service so that we can access the cookies from this method and then we're going to handle um and just return the handle Funk like so here we just return a function this is going to be a higher order function and inside the function the first thing that we need to do is get the current user session and if there is no current user session then we just redirect back to the login so let's do that so let's get session and an error and here we can access the OD service and right now we don't have any functionality to do that but let's call this for example get session uh get user session and here we pass in the requests because we need to access the cookies so let's just go ahead and create this function here so this function is going to be very simple as well so it first receives the request and Returns the goth us user if there is one otherwise it's going to return an eror so the first thing we do is we go to the gothic store has is seen we go to the session name if there is nothing we just return return an empty Gothic user and here we get a hold of the user that we started and if there is nothing here as well for some reason we return an error otherwise we just return the user here let me just handle the error and this is pretty important because if we have an error we need to redirect the user back to the login so let me just have here a loging then we redirect the user to the SL login with the temporary redirect status and we just return out of this function because there is nothing else we can do if everything works if there is a session we can log the session for example here I'm logging the session. name with the user is authenticated just like we did the Callback and at the end we just call the Handler Funk with the request and response like so now to consume this we just go back to the main and wrap the handlers with this function but uh we need to remove this so this is not a method not going to be a service method don't think it makes sense so we can just remove this and make this a function from this package and here on the main we need to wrap all of this with the correct o. require authentication like this like that and then we pass in the OD service and that should be done for all of the Cs service so all of these end points are now authenticated require Authentication and if we go back to the application let's try again and see if we are redirected back so we cannot access any more the application without the session here on the cookies we don't have anything but if we go to the Discord and log in again we should be able to access the rest of the application and here on the cookies we have a session and we can do whatever we want here uh we can search for cars theet cars you can do all of that good stuff but if we delete the cookies and refresh we are redirected back to the sash login so this is the log that we just got here from the application running and the last thing that we want to do is show that we are currently logged in on the application navigation bar so here on the page Temple what I have done here is that I am now receiving a new parameter it's going to be the goth user and then if there is the that information that user information I return uh a log out button the welcome message and then here the image that you saw at the beginning this is going to be the image from the GitHub or whatever provider you have and then here on the other Temple files I'm also updating them because they require the page so I have to be sending in the users so because we have just updated the temple files we also need to update the handlers so for example here on the o we have this error where the home requires now a user so let's pass in the user t coming from the uh the complete off that it was not doing anything there so now it is on the cars it is a little bit different but we can get the user very easily from the O Handler here so get current session very easy pass in the request and we get in the user we get an error I'm going to ignore it just to move a little bit faster and we pass in the user so very easy how easy is this and it's going to be the same for the home pass in the user ex so and now we have um all the errors fixed so let's do just one more final test to see if everything is working let's log in with Discord and now we should be able to be at application where we have our information here updated so guys this is how you secure your goang apis and services and if you like this video if it was helpful consider giving it a thumbs up subscribing and join the Discord community on the description below
Info
Channel: Tiago
Views: 3,388
Rating: undefined out of 5
Keywords: golang auth, htmx, goth, golang, oauth
Id: 9kLZn7qGnWI
Channel Id: undefined
Length: 32min 22sec (1942 seconds)
Published: Sat Mar 02 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.