How to Logout from Spring Security - JWT

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome to this new video since I published the Spring Security with JWT token video which you all liked so much which makes me also so happy I kept getting a lot of messages and comments about how can I log out from such system or from such security system I also asked you to vote for the next video and the next topic which is this one if you didn't see it just go ahead follow me on LinkedIn and also you can join me on Facebook on Discord so you can keep getting these notifications and you can keep getting this information and polling that I'm asking each time so after asking this question you all voted that you want to see how we can Implement a logout or how we can set up a logout mechanism to log out from a backend which is secured based on JWT token we all know that logging out from web application is so important for the user experience and this as well for a backend API so in this video I will walk you through all the necessary Steps step by step and I will explain how we can create this logout mechanism before we start just take one second if you're new to my channel just go ahead and subscribe also enable the notification location so you can get notified each time I publish a new video and also don't forget to hit the like button so I can keep continue on recording videos like this for you guys so this is will show a lot of motivation and will show me that you like my content so this is gonna motivate me to produce more and more content like this for you guys thanks again and now let's start the implementation so before we start any implementation or diving into the code let's first answer this simple question does spring provide any default implementation or any built-in implementation for logging out when we have a JWT system to secure our backend and the answer is when using Spring Security with JWT token there is no built-in implementation for the logout as the stateless nature of the JWT tokens means that the server does not store or keep any track of the user's session and this is the main thing why we don't have a built-in or a default implementation for logging out so this means that the client is responsible of managing the tokens and initializing the logout process so when we talk about front-ends for example the user can just remove the token from the local storage or set or change or set an expiration date for that token so the token gets expired or we can also Implement a custom mechanism on the backend side to secure or to invalidate and revoke these tokens so what spring provides for us in order to simplify and make our life easier is like we have some logout handlers and also some logout success handlers so when we want to perform a logout operation all we need to do is to provide an endpoint and then spring will provide us with a lookout Handler and there we can Implement our Logic on how we want to invalidate or to log out or to let's say remove or unvalidate that token let's not complicate the words and that not let's not also use so much complicated technical words you all remember this diagram that I have right here that explains how the security flow works or how we can secure our API using JWT token so now we need to add or we need to adapt a little bit on this level so in order to be able to implement our Lookout system so first of all let me talk to you or let me explain the idea how we will be implementing this mechanism so first of all we need to store our tokens or the generated tokens for each user and each time the user tries to uh to login or tries to access a secured resources we need to do double checking so the first one we need to validate the token the classic way we know using the jwt2 service and check that the token below belongs to the correct user the user exists and so and so forth and the second check we need to go and fetch the JWT token and make sure that that one first of all exists and the second thing this token needs to be valid and not revoked and not expired so here we have our authentication filter which is right here which is the entry point of the first filter that will be executed not the first filter of the whole application but our first filter that we will be executing once we get the request so before it was only delegating or checking the token using this JWT service which checks that if the token is not expired the user exists and so on and so forth and then now we will be adding another level or another layer or let's call it even another step so also checking the JWT service and also we need to fetch the token repository or we need to fetch the token where we stored it in our storage system and make sure that this token is not expired and also is not revoked because we need to revoke it or message it as expired once the user implements or clicks on logout or perform a logout operation so once we get the execution or the result of these two then we will pass them to the calculation and based of the result so here it's a Boolean result from here and the Boolean result from here and both of them like it will be result 1 and result 2. if true so it's a valid token so we can continue and move on within the filter chain otherwise we need we have an invalid token and we need to send an authorized or 403 back to the user so what we implemented so far within our JWT security video or implementation or mini project if we may call it like that so it was only working around one entity which is the user all right so our user looks like this so we have the user within the primary key ID first name last name email password and we have a role now we need to extend this class diagram and we need also to add a new table or to create a new table where we will be storing all our tokens for the user all right so and for this we will be creating this table right here so it's we will call the token and it will contain an ID of course and then a string token which is unique and then we have a token type in case we want to implement different token type but for now it will be by default a beer token and then we will be having two Boolean Flags the first one is expired and the second one is revoked and we need to update these flags once the user performs a logout or send a logout request to our backend Also let's check the relation between the user and the token so here you see that we have one too many it's not zero too many but it's one too many so having a user in our system means that he has at least one token because if you remember our registration mechanism will automatically create the user and send back a JWT token so we need also to store that JWT token so this is or these are the modifications that we need to implement or we need to do on our backend system in order to realize this mechanism now let's go back to our Anthony J and start implementing this token part so first of all we will start by creating a new entity so just to remind you this is our project and the one we implemented with the latest video the jwd security video so if you didn't watch this yet go ahead and watch that video and then you can come back and implement this alright so I'm gonna collapse everything and here in the main package I will right click and then create a class and I will create it within a package I will call it token and then I will call my class also token all right so yeah it's in this package and here so let's take the same annotation like to make it faster I will just go here to the user and copy these annotations the one that we need data Builder no arcs Constructor all arcs Constructor and of course the entity annotation I'm gonna paste them here so now we need to provide our ID so it's going to be integer and now we need to use the ID annotation and also generated value okay so now we have the ID and as we mentioned before we will be having a private and then string token so it's the token itself and then we will be having a private token type so this is an inam we'll be creating and let's call it token type we can also add this annotation enumerated and here let's use the enum type string then we can create or we can go ahead and create our token type so here it proposes already to create an in-am token type so I will do it and I will create it within the same package all right so here I will just create my first or for for this case I only have this beer token or beer type then I will add two flags so the first one it's Boolean expired and the second one it's also a Boolean revoked so in case you want to revoke manually or for example if you want to implement a mechanism when you restart your application or you start your server you want to revoke all the tokens so I'm creating or I'm preparing the flags for you and here we have also revoked so this is um our token entity now let's add some mapping to it so we said that each entity belongs to one each token entity or like each token belongs to one user so we have here an object user and then user and here we would we know that many tokens can go or can belong to the same user so we have a many to one and now we can add the join column and then we can give it a name so here I will call it user underscore ID now from the other side from the user side we need also to associate these tokens to that user so after the role right here I will create a private list of token and I'll call them tokens so here I need to import my class which is alibo security token and then it's The annotation or the relation one to many and now we need of course the map it by so it's mapped by user so here we have everything or we have our database set up and up to date if you run your application now you should see two tables one for the user and the second one which is the new one the repository not the repository but the token table now we need to create of course the repository for this token because we will need it to access our database and get the tokens from there so here in the same place I will create a new new class and it's going to be an interface and then we call it token Repository so this one of course it should extend the jpa repository and here we need of course let me make this full screen for you so it's of type token and then integer our ID all right so now after that I would need two method I would need to create two methods the first one is the method that we that will allow us or help us to get all the valid tokens for specific user so we pass the user ID and based on that or using that we can get all the tokens that belong to to this user and the second one is just finding a token by the token itself so all I need is just to pass the token string which is unique and then I would need to get the token from the database so the first one I will call it list of token and then I will call it find all valid tokens by user and here I need to pass the integer user ID or ID all right so here this is the user ID and for that I will use the query annotation to create my my query okay so it will be select T from token t and then I will inter join it or like I will create a join user U and then I need to add my condition like what is the join condition on here I have t dot user dot ID not email equals U dot ID or the user dot ID so this is the the query and now I need to add the condition that the token or the tokens belong to that selected user so now I will add my workloads and where U dot ID equals user ID the one I have as a parameter right here and now I will I want all the valid tokens so valid tokens means that expired false and revoked false so so this is or this means that this token or those tokens are valid and now I will do T dot expired equals false or T dot revoked equals false so in this case I have a query that will allow me or that will help me get all the queries all all the tokens from the token table which are not yet expired or revoked so which are valid so this is the query for it maybe let me format it a bit now we will create the second the second method so the second method is quite easy so it's going to be an optional so it's not find by ID but it's find by token all right and here I need to pass string token so now I have my two methods that I will need later on in order to implement my logout mechanism and now as a next step we need to save or persist any generated token by our system so to do that just to remind you that all the generation happens on this level so on the authentication we have our authentication Service which get gets called by the authentication controller and in here we have the two methods the first one that creates or register a new user and the second one that authenticates a user so let's go ahead and implement this So within the register method here after generating the token and saving the user what we need to do we need to create or to persist that generated token into our database alright so to do that I will create a VAR token equals token dot Builder and then build and for this I have first of all the user so I need to set the user so now I need to save this one a saved user or the persisted user I need to save it into a variable all right so then this one is going to be saved user and then the token here so I have the token which is this JWT generator token so it's JWT token and then I need to set the token type which as we mentioned it's a token type beer token and then after that we have the two flags expired and revoked when we create them we need to set them to false because when we generate a token it's not revoked and it's not expired yes yet so revoked Falls and then expired also false so here we have our token and then we need to persist the token to the Token table so to do that of course we need to inject our token repository so after this user Repository I will have a private final token repository and I will call it token Repository and now going back here so now all I need to do is token repository dot save my token all right so this is on the register level all right now we need to implement this also on the authentication level when the the user gets authenticated and everything is alright and we are about to return the generated JWT token we need also to store that so now we see that creating the token will be the same code AS here so let's extract this block of code to Method so you can select the code and then you can do right click on here and then you have introduce or extractor method all right or you can use the shortcut if you are if I use two shortcuts let's call our method save user token and it takes the user and the JWT token all right so let me just refactor it to avoid or just rename it for this typo and I will just rename this one and call it user and this one it can stay as JWT token all right so here we have our refactoring so we have the save token and now within the authenticate method after we authenticate the user we get everything and we generate the token we need also to save user token and I will pass the user and the JWT token also as parameter and before moving forward with the implementation and writing a lot of code let's first start our application and let me show you the changes that we have so far so I will start the application right here and then first of all let's go ahead and check our database and see what we have with the as tables so this is my database I just refreshed it and this is my JWT security and here I see that I have two tables and I have the user and the token all right so from the database level everything is fine now I'll just clear everything and open my Postman and I will just invoke or register a new user so let's open Postman right here so here I have the query already ready this is the one we used the last time too so it's not nothing fancy or new so here we have the register endpoint and we have our body right here so all I need to do is to click Send so what I'm expecting here is this token so it's okay I have the token now I I want to go and check the database and what I'm expecting is to have this token already registered so I'm gonna open this and first of all let's check the user so here we have our user just getting created and we have the email password and so on so forth now let's go and check the token so for the token right here we see that we have this token it's expired false and not revoked and the token is this one and we also we have the token type and this token is associated to that user now what I will do I will also go and try the same method but I will try to authenticate the user instead of registering the user I will just authenticate him many times alright so I will send the first time so I have a new token I'm gonna send another time I have also another token and for four times now I will go back to the database and refresh this table so we see that we have many tokens assigned to the same user but there is one thing which is not good that we have multiple multiple user usable tokens so as you can see all of them are not expired and not revoked but what what I expect is for one specific user for the user ID number one I would like or I need to have maximum one available or one valid token and the rest of them they need to be expired and revoked at the same time alright so what we implemented so far is fine now let's just go and revoke all the existing or all the other valid tokens of a specific user and to demonstrate that let's stop our application and first let's implement this and then we can continue now I want to implement the method that will allow or that will revoke all the existing tokens for a specific user first let me move this method to the bottom and now I will create another method private void and then we call it revoke all user tokens and this one it will take a user as a parameter so the user as a parameter here I need it just to fetch all the tokens and all the available and valid tokens for a specific user in the database all right so here I have um valid tokens I'll create valuable call it valid tokens equals token repository Dot find all valid tokens by user and now I need to pass the user.getid let me call this one valid user tokens just to be consistent so here if I will just make a check if valid user tokens Dot is empty so if it's empty I will just return and I don't need to execute anything all right so we call this an early return in order not to execute the rest of the of the method otherwise what I what I need to do I will I need to Loop over this valid user tokens dot for each and here let's say t for token and then I want just to update or to uh to modify the Vari the values okay so here set expired true and also I want T Dot revoked true all right once I update all the tokens and revoke them what I need to do is token repository dot save all I want to save all the valid tokens and that's it okay so now before saving the user token now we are back to this authenticate method and before saving the user token we need to revoke all of them and you may ask why before doing that because if you save the token and then call for revoking the tokens even that new token will be will be also revoked so let's go let's call our method here and now we have our user as parameter all right now let's let's test these changes I restarted the application and now let's go back and open our Postman and let's try this again so first of all let me duplicate this uh to have one end point for authentication and the other one for register let me register user and now I will click on send so here I have my token and if I go back to my ntdj and open my database I will see that I have the token already saved for that user let me make this full screen so we have the token saved for that user or for the user number one we just created and now if I go back and do authentication if I authenticate I will get a new token but if I go back to the database and refresh this table you will see that this old one is expired and revoked and the new one which is just got generated is still available if I do authenticate again and again and again and again and now I go back to the database I will find only one token valid which is the last one so now we have almost everything set up and ready to use but we still have one issue which is if I pass a non-valid token a non-valid for our system for our database which is expired true and revoked true but if this token is still valid the the request will pass through my application or through my backend and it will return a response and why is that because on the JWT level or the JWT authentication filter level we did not include this logic yet so we only here let me remind you what I'm talking about I can close this class right here and if I open my config and if you go to the JWT authentication filter and I'm gonna make this one full screen so you will see right here that we are only relying on the JWT service to valid the token but what I need to do is I need to go to the database fetch the token and then check if the token is valid or not all right so let's let's implement this but before implementing this let me show you what I'm talking about let me show you in action what I'm talking about so here I will use the method this one let me just put it next to it to the other so first of all I will create a user so this is my user and if I open back my table and my database in here if I open this token table you see that we have this valid token all right so I'm gonna copy this one and I will try to access my demo controller endpoint so I need to update the token right here so I will paste the new one and click on send so you as you can see here we have the response from the backend hello from secured endpoint okay now if I go back here and generate a new token so now I have a new token but my request still using the old one and the old one if we go to the database and check the old one is revoked and and expired and for that we need also to add a double check and make sure that the token is no longer valid and that token is no longer usable to access my backend so here as you can see it's revoked and expired and if I go back to my Postman and I'm using that old token and I click on send I still can access the backend which should be totally forbidden so let's implement this I'm gonna close this one make make it full screen now I can stop the back end so now on the JWT authentication filter class first of all I need to inject a simple like I want to inject my token Repository let's call it token Repository and then let's make this one final and then within this token repository I want to access and use the method that we created before that so here at this level after checking that we don't have that we have the user mail and the user is not authenticated and here if we are fetching or we're fetching the user details from the database using the user Detail Service and at this line right here we are checking if the token is valid or not so at this level we need to double check that also the token is valid on the database side so I will create a variable I will call it also is token valid all right so this is token valid equals my token repository Dot find by token here I have my token which is the JWT and I will fetch or try to fetch the token from my database and for example if I use a token that does not exist the system will not allow this alright and then after finding the token by token all I need to do is I need to map so for the mapping so I'm gonna call it t and I want to return T dot expired actually I wanted not to be expired and not to be revoked and not t dot is revoked and then or else if I don't have anything else I want to remove always not remove sorry I want to return always false so this method let me explain it to you so we are finding or are trying to find the token by its token by the generated token and then what I what I'm doing right here is I'm mapping this result to a Boolean because I need a Boolean right here to check if the token is valid or not so for this I'm just mapping it to it should not be expired and also it should not be revoked otherwise if my result or if I can't find a token by the token that I have or buy this JWT token string I will be returning false anyway all right so now all I need to do is here so first of all I check that my token is not expired belongs to the user and so and all this process that you explained before if true then I need also to check that this token is ready or available on the database alright so also it should be is token valid right here so that's it now we updated our our system let's go ahead and let me show you like the changes or let me show you that this mechanism now is more robust and more powerful to validate tokens all right so I'm gonna restart the application and test so the application is up and running let me open my token table which should be empty right now and I will use my Postman and first of all I will register a new user so I get this new token and I'm gonna copy it and ensure you or show you that this token is valid to access the backend so this is an old one all right I will not update it yet if I click Send so I'm getting 403 because this one is old and if I remove and use the new token and the click Send so I'm getting a response from my endpoint which is Hello from a secured endpoint now I will keep the same token within this get method and I will go back to my authenticate method or authenticate endpoint and I will try to authenticate again the same user so I will get a new token for that user so I'm clicking on send so now I have my new token and also if I show you on the database level I only have one valid token which is this one okay but the old one that we used for to to fetch or to access this get endpoint now will be invalid and we should not be able to access or to have this response message alright let's click on send and now as you can see we have this 403 Forbidden so it means our mechanism is working okay everything is working fine and now we are relying on the token itself and also we are relying on the database to check that that token is already in our database and it's not expired and not revoked okay so now let's move on and really check the best part of this uh it's not over yet don't go away because now the best part will start so now I just want to talk you talk to you as in full screen so here you can see uh See Me all and now I want to tell you how we need or how we are going to implement this logout so as I mentioned in the beginning of this video that spring already provides us with a logout Handler and also a logout success Handler so we have two methods or two um let's say two bins or two implementation that will help us to implement our log out mechanism all right so what we did so far is just preparing everything in order to implement this logout endpoint all right so now let's move on and start implementing this so now first of all or the first thing that we need to do we need to add some configuration to our security configuration in order to tell spring that we have now or we want to implement a logout mechanism or a logout functionality so here if you let me make this full screen again so if you go to this security filter chain bin when where we defined everything so all we need to do is after this after adding this filter or the JWT filter I want to do something or I want to give more or extra configuration in order to tell spring that now I'm working or I want to do a logout so here if I do DOT log out and then I do add logout Handler so for this Lookout handle for the moment I will put it as null and I will tell you what is this logout Handler so it should be an object of type logout Handler and it should not or it should not be null all right and then I will add also I will add also a log out success Handler and for this Lookout success Handler it's just a Lambda expression so let me use the auto completion right here and this is what will happen or what you want to do once the logout success or like once you have a logout success so for our case right here all we want to do is to clear our security context so if the user is logged out we need to clear our security context in order the user cannot access again with this expired token he would not be able to access again our API so I will use security context holder dot clear context and that's it so let me maybe make this in the new line so you can see it and this is our logout success Handler now let me go back to this logout handle or add log out Handler as I mentioned this look out Handler it will use or it's it's waiting for a log out Handler object and to do that this is where we need or where we want to implement all the logout mechanism all right but before that here we mentioned that we need that we have the logout Handler we have the logout success Hunter but we did not specify the logout URL yet for the logout spring uses a default URL which is slash logout but if you want to have your own log out URL you can just add it you can just override it in our case we don't need to implement an end point or to add a new method in any controller all we need to do is here just to tell spring that our logout URL is the following all right so for this I will be following the same nomination or the same naming that we used for our application so it will be slash API slash V1 auth slash logout all right so now if we use so we have our auth controller which contains the register the authentication also the authenticate method now I want to add a log out but I will adjust the URL and I will tell spring every time you get a request for this specific URL API V1 auth log out just implement this log out Handler or execute this log out Handler and do not delegate it to any of our controllers so here since we said that this add logout Handler is expecting an object of of type log out Handler let's go and create this log out Handler in our config package I will create a new class and I will call it logout service in here and this logout servers should implement the logout Handler alright so this logout handle is an interface let me implement the methods and may let me make this full screen and of course I will align these variables for you so you can read it correctly all right so we have this logout Handler and this logout Handler has already a method called logout and it passes as parameter the request the response and also the authentication in case you want or you need to get the user information or the user data beforehand now I need to make this service and of course I will need the required arcs Constructor from lombok in order if I need to inject something okay so now once we created this logout server a service let's map it or let's bind it to this logout Handler in our security configuration so scroll to the top and here I will create a private final logout Handler and I will call it just look out Handler and spring automatically will find that we have a logout Handler implementation and it will use it so going back here and in here just use my logout Handler so after binding the logout Handler servers or or our logout service let's go to this logout method and let's implement it so let me explain you a bit how it works so as you can see here in our authentication filter what we do first is is we extract the JWT from our authorization header all right so we have the header and we try to extract the JWT and based on that we check if the if we have the user and so on and so forth now it's going to be something similar to that so as you can see here I will just copy this lines of code and I will explain you here what what I need to do so this logout method as I mentioned it has or it passes as parameters the request response and the authentication and for us or for our case we want to invalidate the token so we need to extract or to get the token from the request right here and then we need to fetch this request in the database and unvalidate it and then the JWT authentication filter will do the job since we updated our mechanism or our implementation there so first of all I'm gonna paste the code right here and I'm gonna perform some cleanup I don't need the user email and now also I don't have the filter chain so here my only test right here if I don't have an authorization header and this header is not a beer token so I don't need to implement anything or I don't need to do anything at all alright so then I would need to inject my token Repository and let's make it final of course and now what I need to do I will create VAR and I will call it stored token equals my token repository dot find by token and my token is the JWT token variable that I have right here or else I will just return null all right so just to make it as easy as possible so once I have my store token I will perform a small check if my stored token is not null this means if my token or the token that I got and the header is valid and in my database of course so I just need to unvalidate it all right so then what I need to do stored token dot set expired to true and then stored token Dot set revoked also to true all right and then what I need to do just call my token Repository dot save this stored token and that's it and like this I have almost my Lookout mechanism set now let's move on and see what comes next now I will restart my application and let's show let me show you the changes that we that we did and what are the impacts and how this logout works so my application is running I'm gonna clean the console and open again my Postman right here so the first thing that I need to do is of course to register user and then I will try to access some endpoints so I will copy the token from here and then I will post paste it here so this token now will allow me to access the backend because it's valid not revoked and so and so forth now what I want to do is I want to perform a log out and as as you remember the logout URL is slash API V1 slash look out now if I click on send I have first of all I have a 200 but first let me check my database and see if my token is already revoked or not so now this token is not revoked and the reason why is here within this logout URL I'm not passing the correct one of course like I was just checking if you are following or correctly or not so now I'm just pasting the new token and I will click on send and you see that we have 200 right here and again if I go back to my database update you see that the token is expired and also revoked at the same time now if I go back to this get method and I click Send I should or I expect a 403 and this is what we get right here we have our 403 unless I do a an authenticate again to generate a new token and I can use this token to access my back end again so let's give it a final try and send so here we have the the endpoint or we have the response from our endpoint I will log out the user and I will click on send again now if I try to access this so it's again forbidden now I will final check on the database level so we have two tokens and both of them are expired and revoked again thank you for watching and thank you for staying at this level I really hope that the video was clear for you and you learned something new from me if you like this video and if you like my content and if you are not yet subscribed to my channel just go ahead and click the Subscribe button and also take just one second it's only one second and hit the Thumbs Up Button give me some motivation so I can continue on producing and creating content like this for you guys I was so happy having you here and see you in the next video
Info
Channel: Bouali Ali
Views: 11,450
Rating: undefined out of 5
Keywords: spring, jpa, data jpa, mapping, onetoone, one to one, spring data, many to one, manytoone, class, generatedvalue, persistence, repository, service, jparepository, jpa repository, uml, class diagram, design, software, engineer, software engineer, java, jakarta, javax, spring boot, springboot, security, spring security, aliboucoding, spring boot 3.0, spring boot 3, spring 3, jwt, filter, authentication, authorization, bearer, jjwt, oauth2, github, social connect, social login
Id: 0GGFZdYe-FY
Channel Id: undefined
Length: 50min 8sec (3008 seconds)
Published: Tue Feb 21 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.