Godot Multiplayer - Network Token Verification | Godot Dedicated Server #6

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this godot tutorial i'll teach you how you can generate and use verification tokens to ensure that the players trying to log into your game server have been properly authenticated let's get started in episode number three of this tutorial series i'm showing you this network architecture diagram and explain to you how the authentication server on a successful authentication will generate a verification token that it will distribute to the game server and the client the client will then log into the game server using this token by doing so we can create a direct connection between the client and the game server cutting out any latency that would otherwise be created by having the gateway and the authentication server in between now of course the game server has to validate that token on that login attempt and we want to make sure that those tokens are only valid for a set number of seconds avoiding any exploits so in this tutorial we'll generate this token we'll do the distribution to the client and the game server make sure that is used in a login process from the client to the server will validate it on the game server and lastly we have to account for a number of situations that the client may have a faulty verification token might have a timeout of the connection during that verification process and we want none of those situations to crash our server so we have to account for those let's get started let's dive into generating that verification token i'm right here on the authentication server where we have our authentication function from the previous tutorials where we check username and password i've already added a new variable on the top for the token right here we got our lines of code to actually generate a verification token and as you see i put a couple of prints in between to demonstrate to you how this actually works what we first do is we randomize the seed of the random number generator which we use to generate a random number between 1 and 2 to the power of 32. that's a big number so we're going to get a completely random number which we first convert to a string and then we're going to apply the hashing function that is going to return a 64 character long string it doesn't matter if the runner number is 1 or 10 billion the amount of characters that this function outputs is always going to be 64. so with that hash random number we now need a timestamp and for the timestamp we're going to be using the operating system getunixtime the unix time is the number of seconds that has passed since midnight january 1st 1970. that's right that's the number of seconds that have passed over the last 50 years that is going to be a big number we're about at 1.6 billion right now but those two numbers which comes out as an integer are going to be super easy to compare we can compare the current time with when the token was generated very easily identifying whether those 30 seconds have passed or not so that will be used by the game server to invalidate tokens once a certain amount of time has passed so finally we take the token is going to be smashing these two integers together so we're going to get a 64 character long hashed um variable here so this is 64 characters and the timestamp is going to be 10 characters now you might be wondering well that's 10 characters but couldn't get 11 characters well yes but we took 1.6 billion seconds to pass the last 50 years so before we gonna get to 11 billion or above 10 billion i should say there's going to be another 200 years at least so unless you want to really try and make your game stand the test of time and still expect it's going to be played 200 years from now you don't have to worry so once we get that we also define the game server and we're going to be distributing the tokens now to distribute the tokens we are actually going to be needing a connection between the authentication server and the game server and we've never really established that connection in previous episodes so we have to do that first however before we do that let me first demonstrate to you how this works i got all my servers running already and i got a client right here which i'll just turn off and we'll start up a new client and we'll log in and of course our authentication server is now going to be doing its thing so you see we have a random number that starts with eight one we have our hashed uh result of hashing that random number then we have our timestamp which ends in 384 and then we smack the two together to get our final verification token if on where to run this again i can start a new client i log in go to my authentication server now you see our random number starts with 585 the hashed function looks completely different now we have the amount of seconds and from 384 we went to 407 and we smashed them together so like that we have generated our random number now let's look at that distribution well future stefan here this is of course all dragged out and print commands and everything for demonstration purposes so you understand how this works you can of course very easily replace this with just one line of code while you smash all of that converting and hashing and randomizing into a single line of code to make it everything just a little bit more elegant okay back to you stefan right now for the distribution of these tokens as you can see i'm already calling the game servers singleton right here and that's a singleton you have not seen before in this tutorial episodes what i've done i've created a new singleton which is now an auto loaded script of course and under port 1912 i've created a new custom multiplayer api so that this authentication server can not only connect to the gateways but can also connect to the various game servers that we'll have in our game so this function is going to be exactly the same as the function we have on the gateway for the peers the clients so i've explained everything of how this works how you can create a custom multiplayer api in episode number three of this tutorial series so i won't go into too much detail of course when we have a look at the game server here we now have the singleton the hub connection the authentication server is going to be the hub for all the game servers so here you see basically exactly the same as you have on the client to connect to the gateway again i'm not going to be taking too much time to go into this kind of details back to the authentication server what we do here is that as soon as a pair connects so that pair is going to be a game server we're going to be saving that game server in a game server list which is a variable on the top here and we'll simply give it a name and we'll save the game server id so that when the player connects to a game server once we have selected the game server we actually need to know which peer id that was so we need to keep a list of all the game servers with their respective peer id so that we can call them properly so that is what this little function does right here and that is where this game server game server one comes from as soon as we have programmed in multiple game servers with load balancers then of course these functions this hard code game server one here has to be replaced so what we do we call the singleton game servers and we're going to call the function distribute login token it's as easy as that we take the token which we generated and we take the game server which is now going to be game server number one over to the singleton that function will be the function right here we're going to be determining what the game server peer id is for that we go into the game server list with the game server name that was provided by the authentication function and we're going to of course find this dictionary right there which has that game server id now with that done we can call the rpc id game server pair id that we determined there we call the function receive login token and we send that token over to the game server now going to the game server and going to our hub connection that we created we have a very easy remote function that basically is gonna call the game server so that is going to be the main note i've got that on ready for game server get note root game server that's the main node of this server we're going to go to the game server and we have a variable there called expected tokens and we're going to append the token so these are the tokens that we expect the connection from so that's why this is called expected tokens that expected tokens is nothing more than the conventional game server that we've programmed in episode number one of this tutorial series and we got the expected tokens right here as you can see i've added three tokens just basically to teach you and show you and demonstrate how this works when multiple clients would be trying to connect at the same time so we'll get into those in a moment and then we'll delete them afterwards to show you how it would work if that's completely cleaned up so that's the distribution to the the game server so now the game server has a number of expected tokens from the authentication server going to the authenticate script where we have just generated that token we also have the rpc id here that calls the gateway to return the auth authentication results to the player so in the previous episodes we only have the result and the player id but now i've added the token on top of that that also means that you have to add that token as a variable onto the gateway then from the gateway to the second singleton on the gateway that communicates to the player and then of course the player needs to be receiving this as well so when we go to the player script on the gateway we now see that on our remote function return logging request which is a function we already programmed and i believe episode number three yeah it was free we now have that token on top of it but that went through the gateway so you have to add that variable on the gateway scripts as well so right here we're going to be setting that token if that result was successful so the result is the result of username and password and then we're gonna call the game server singleton which is still on the client which has now the token variable on the top here and we're gonna set the token to that variable and then we're going to call the game server connect to server that's not new that was already the case what is new though is that before we were already deleting the login screen from the world so the login screen would already be gone but now we want that login screen to stay because first we need to verify that we actually have a valid token so now we only want to delete that login screen the moment the token has been verified successfully so that has been replaced to a function on the game server where now we're going to be using the verification of the token first so with that said i think we now distributed our token our player has the token and our game server as the token now we gotta look at how we're actually going to handle all these tokens on the game server and let's program that invalidating function first to make sure that the tokens don't stay there forever so i'm right here on the game server under the main game server script where we have our expected tokens the expected tokens that we need to be checking whether they need or need not be invalidated so we could build a invalidation function under the process function that runs 60 times per second but i don't really care whether it's 28 or 32 or 35 seconds that we check it all i care about is that we check them about every 30 seconds so it would be a waste to check this 60 times per second because just gonna be using server resources and i prefer the game server to use these resources somewhere else no making sure players don't have lag for example so instead i've added this timer node to the main note here to the main game server as a child and i've renamed it token expiration i've set it up to continuously keep counting so as soon as this counts down from 10 seconds to zero it will automatically restart again and again and again and it automatically starts when the server starts on the node signals i've connected this to the on token expiration timeout signal on the script so when we go now to this function this function is going to be triggered once every 10 seconds which is great for our invalidation process so how do we invalidate things we have the current time which is going to be again the operating system get unix time and we need to compare the current time to the time of the token so we need the token time if the expected tokens this array on the top here is empty we prefer that it does not do anything at all and we simply pass and wait for the next trigger after 10 seconds else if we do have tokens we need to check them first we're going to be going over every single token by um range expected token size that's the total size of the array minus one minus one minus one what this actually does is well basically what it counters i can better explain if you go through an array and you determine that one um a token in the middle needs to be deleted all the tokens are going to be shifting one index position up to the beginning that means you are possibly skipping over some of these tokens while you're deleting other ones out to counter this the best way to approach this is by going through the array backwards or reversed as that's called so by starting at the end if we delete anything out we don't risk at shifting any of our tokens in their index number because everything that will shift because yeah still indexes will shift but all of those are already checked in this 10 seconds and we can just continue going forward so with that set we're going to take the token time and what we do we take the expected tokens and then the i so the index that's going to return the 74 character long token and we're going to take the right text from the 64th character so it's going to skip 64 characters and then it's going to return us character 65 and onward and of course that's up to 74 and those are the 10 digits of our timestamp which we need of course so that is the token time that is an integer and we have to convert that because the expected tokens starts as a string and then we simply deduct the token time from the current time and if that is any bigger than 30 seconds it means that token has been generated more than 30 seconds ago and we invalidate it to invalidate we simply take the array and we remove it from the array and garner this if a player at that point tries to log in with that token it's no longer recognized by the game server and says sorry you're too late so now our game server has all these expected tokens all validated now we need to do something with it we actually need to verify a player so i'm here on the client side on the player side and this is where we are immediately presented with a challenge because the actual connection to the network is made online number 16 here but as you can see there's no place for us to put an extra variable we can't put that token in there so the client is actually going to become a pair of the network and then the network has to request that player to properly verify itself so we actually initiate the verification process from the game server which feels a little bit counter-intuitive however in the previous episodes we already had this part right here this is basically whenever a pair makes a connection to the server the pair connected signal is fired and we fire this signal and we were starting uh the start function under the player verification node that was a node reference right there so that is basically still in place we've just changed it a little bit first what we have is we have this big yellow box and we immediately created the player container without any questions asked basically well this is what we have to delete and we have replaced that with these two lines these two lines start with a new variable on the top which is a dictionary awaiting verification this is a little bit of a waiting room in air quotes and this is going to keep track of all the players that are pairs of the network but have not had their tokens successfully verified yet the reason why we have this waiting room is because we don't want players or want don't want to keep players too long in that you know between two places kind of a situation because as long as the player is in there the login screen will show a big sand walker or beach ball spinning of death doom thingy and the player is just going to look at that and say like hey this is not working or what could also happen is that the player were trying to exploit and for some reason it stays in the waiting room but that player that was trying to exploit and is still connected to your network is taking up one of the concurrently connected peer seats and of course if you have a limit to the maximum number of pairs connected to your server you wouldn't want players to take up those seats while just staying in basically between two worlds so with all of that said what we do when we start this function and when it's basically when the pair connects we first append that awaiting verification dictionary with a key we use the player id as a key and we give it a timestamp so that later we can check whether the player has not been in there for too long then we call the main interface and the main interface is simply to get parent so that will be the main game server note and we run the fetch token with the player id on the game server note we have that fetch token which calls the player through rpc id and asks the player to run the function fetch token switching to the client side we have the fetch token remote function right here which immediately calls back the server with the return token function and it will send over the token that we saved in the variable on the top and that of course came from the return login request where we did the game server token is token so with that said let's now go back to the game server so we're gonna run the remote function return token first thing we do is we identify who was the player that was actually sending us this token because we can have several players logging in at the same time of course and then we're gonna call the player verification process node again and we're gonna run the function verify with the player id and the token so this is when the actual verification happens okay the way we start is we're gonna assume that the token verification is false that's the safest thing to do next we have a well function and this requires a little bit of explaining what we do is we're gonna take the current time and we're going to check that with the time of the token that was provided by the player so not the token not the expected token that came from the authentication server this is a token provided by the player so we're going to check that timestamp instead of checking whether it's bigger than 30 seconds we check whether it's smaller than 30 seconds and here is the reason why the first thing we do is we check if the main interface so that will be the main server where we have the expected tokens variables if it has that token but it's a big but there's a very small chance that that token is not yet received by the game server of course the path from the authentication server to the game server is much smaller shorter much smaller shorter then from the authentication server to the gateway to the client then to the game server then the request for the token and we're again back to the game server so it's likely very likely even that the game server has received that verification token from the authentication server before the player tries to make the login attempt or the verify attempt of that token however if the connection between the authentication server and the game server pings hangs times out if the connection or that packet that was sent is corrupted and needs to be resent again and again because some of those packets were missing there is a chance that that game server has not received a token yet but the token provided by the player is valid so because of that and because the expected tokens on the main interface are valid for 30 seconds we're gonna give the player 30 seconds time to successfully verify that token so if we cannot verify it successfully we're going to yield the code for two seconds and then through this well function is going to run again and again and again so with 32 seconds we're going to do 15 attempts to see if we can verify this token after 15 seconds we're going to say sorry we tried but we really couldn't please try again because probably something went wrong so what we do if we have a successful verification first of all we set that variable token verification the start as false we set it to true this is the moment we create the player container that we talked about in episode number four i believe the player container then we are going to be erasing the player id from the awaiting verification dictionary so we're basically gonna kick the player out of the waiting room and that's of course true because it's successfully verified now and we're going to be erasing the token from the expected tokens on the main interface on the main game server out of this array right there and we do that because of course we only want one connection to be able to be made with one token so there's no reason to keep that token in the open there who knows maybe the player is working with two phones at the same time and clicking logging at the same time with the same token of somehow or some way the last thing we'd want is to have two players connect with the same token that would be weird um that would be exploit sensitive so we're just going to erase the token asap the moment it is used okay with all of that set we now have an outcome whether it's immediate after a little bit of a time or we have the outcome after 30 seconds that we couldn't verify nonetheless of what the outcome is we're gonna call the main interface node we're gonna return token verification results and we're gonna take the player id and the token verification so what this does before we get into those last three lines there we're gonna go back to the game server this function return token verification results is played we are going rpc id called the player we're going to call that function we're going to give it the results that's true or false back to the player script we have this remote function right here if true then this is the moment we're going to quick free the login screen so that is where we have replaced it too as i mentioned earlier in this tutorial we have a successful token verification otherwise it's filled and then we want to enable that login button again so the player can make a second attempt okay so that is a successful login but what if it goes wrong so back to the player verification process back to the verify function if the token verification is false and it could be for two reasons simply because well the internet had a hiccup and the game server never received the token from the authentication server but it could also be when players try to type in their own key if they were to recompile your game the executable and when we do the fetch token here which is called by the game server instead of the token they were just gonna you know type a code here and say well see if a different token will work well when they do that then this function is going to immediately sort of say well that is not true anymore so we immediately go in here and this is the moment that well this is to make sure that people are disconnected immediately after providing a dodgy key that's sort of what happens however there is another situation we'll get to that in a moment that's another exploit and that's where this last function here is for if this happens so that could be dodgy could be not dodgy can be both ways um then basically we're going to be erasing the player from the awaiting verification so we're going to kick it out of this dictionary and we're going to be disconnecting the peer from the network so basically we just kick that player out the player has received the results so the client is aware that the result was false and the login has been unlocked again so the player can make a second attempt if he wants to alright with that done there's one last thing to do because if the player and this is another exploit possibility if he would provide a token and he understands how the token works maybe he has sniffed that out with some packet sniff or some sort and he knows look the token is expecting the 74 character string and the last 10 are the unix timestamp okay let's say the player knows that and he puts a timestamp a year from now instead of the current time so what that would do is it would make this wow smaller than 30. yeah absolutely because now suddenly that token is going to have a way bigger value than the current time so the outcome of this subtraction is going to be negative a million for example and that is of course smaller than 30. that means that that player would stay forever in this verification function through this yield function right here and it could stay there for a year taking up a seat in your game and like i said you have your maximum concurrently maximum concurrent uses and you don't want that play to take a seat for too long so that is where we're going to take that timestamp that we put in the awaiting verification dictionary and we're going to make sure that our player is still being kicked out and that what happens under this function right here this function right here is initiated by a new timer again a new timer under the player verification script this is a timer node i've set this up exactly the same 10 seconds auto start on node connected the signal so this function is going to run every 10 seconds we again are going to take the current time and we're going to take the start time the start time in this case is the start time for when this peer connected so we're going to do exactly the same if a waiting verification is empty and i mean the same it's almost the same function as the token expiration function right here just a little bit different so if it's empty we do nothing if it's not empty then for every key in awaiting verification and remember these keys are the keys we use here and those are actually pair ids player ids so that's very important to to note there for every key we're going to check what the start time was we simply just retrieved it out of the dictionary because we put it in there with that timestamp then we're gonna deduct the start time from the current time and if that is anything more than 10 seconds then we're gonna erase the key out you can also make this 30 seconds i think that's where i was i think i changed it for testing purposes so if there's more than 30 seconds then that way the player has been in the waiting room for 30 seconds we're gonna be deleting that player from the array now there's another thing happening here because now we of course also want to disconnect the pair and we want to return the verification results because we do want to tell the player like hey you're logging a tent field however it could be the case under some circumstances that the player was from the entire client maybe through maybe he lost his wifi or maybe he lost his signal altogether and in that case it could be that this function runs in a moment that that player is no longer connected and if you're going to try and disconnect a peer with a id a peer id that is no longer connected to the network you of course going to get an error so the first thing we have to do after we have kicked him out of the waiting room we first have to retrieve all the pairs that are currently connected to the network and then if we have that key so if the pair still exists on the network then we are going to return the token verification and we're going to use the key as the player id and we're going to return the result false so that is the same function we run here and it's the same sort of false but then imitate it as there and we are of course gonna disconnect that player from the network just like we would disconnect it here now with all of that done we have two print commands right right there so we can check what's going on but right now we have the entire code that we need to properly generate that token distribute that token verify that token and counter a number of exports or situation that could happen on the internet so all we have to do now i guess is test it so to test this i've maybe not put enough print commands into the various code elements so we can't really look at the whole process all together as we did in episode number three but nonetheless let's look at if we can actually get a successful connection going first we're gonna go to the game server and i'll explain these tokens that i've added here they're dummy tokens are just for testing purposes i've added a seven instead of a six here or i've replaced the six with a seven so it's like two three years in the future and this third token here that so that's an old token and you can already see the server is running you can already see it immediately deleted the the first token or the third token i should say it leave that out of there and only the two tokens with the seven uh remain so that is working just fine now going over to the client we'll just rerun the client we'll log in again and on the client we can immediately see attempting to log in etc etc and right on the bottom successful token verification and we can go to the game server and we can go to the remote explorer and we can see that yes indeed we do have that peer connection and we do see that that is indeed the user that connected so that's another player verification that create player container that was done properly we did have that token for a moment even though we don't see it in this prints that's just because well the whole process was done within 10 seconds between those two timers hitting off so everything works fine and uh good luck copying this into your project that was it for today guys hope you liked it if you did smash that like button hit subscribe don't forget that little bell icon to make sure that you don't miss out on the next episodes in this tutorial series in the next couple of episodes we're probably going to be shifting a little bit to security very important for the fundamentals of a proper network architecture we're going to be having a look at how we can encrypt our passwords we're sending over the network using ssl certificates and we're going to have a look at how we can sold the password let's salt you know from salt and pepper to protect ourselves against rainbow tables now what those are and how we're going to do that is going to come up in next episodes i hope to see you there until then keep on gaming keep on coding see you later guys
Info
Channel: Game Development Center
Views: 4,728
Rating: undefined out of 5
Keywords: Godot Multiplayer, Godot Network, Godot Multiplayer Tutorial, Godot Token, Godot Network Token, Godot Network Token Verification, Godot Multiplayer Network, Godot Server, Godot Dedicated Server, Godot Multiplayer Server, How to make a multiplayer game, Godot MMO, Godot Beginner Tutorial, Godot 2d Tutorial, Godot Tutorial, Godot
Id: YOmMWP_8r9A
Channel Id: undefined
Length: 31min 9sec (1869 seconds)
Published: Sun Oct 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.