After initial user authentication. JWTs can be used for verification at REST API endpoints. In this tutorial, you will learn how to issue access and refresh tokens (JWTs), and also the recommended way to issue these tokens for security concerns. There will also be suggestions for storing these tokens in your frontend apps. If you are just starting out with Node.js and Express, I suggest you start at the beginning of the Node.js for Beginners tutorial series here: ruclips.net/p/PL0Zuz27SZ-6PFkIxaJ6Xx_X46avTM1aYw
I've been following your tutorials on Node, Express, and working my way to Mongo for my project this week. I've told my professor about how helpful your videos are and I've been spreading the word to my classmates. I'm surprised you don't have more views on these tutorials. Thank you for your help!
I usually stay away from tutorials but the content you post is insanely good, I have actually learnt a lot from your channel. Since I discovered it my skills with javascript have improved a lot. Thank you so much
This is masterpiece! 😍 I've thanked on all videos of you I've watched in the comments, and I will do it again on this one because you deserve it. Please keep creating content, I'm seriously worried you might lose interest in it at some point and we lose more content created by you, because you're so good.
be very careful when testing this specific implementation with nodemon vs a normal server. nodemon restarts after any file changes, including changes to models\users.json. This allows each function to get the updated state of refresh token. If you run this without nodemon, your refresh and logout functions will reference old versions of the users.json data that do not include the refresh token from the auth. This causes some unwanted behavior that took me quite a while to catch. As always, thanks @DaveGrayTeachescode for another great and comprehensive lesson
I just wanted to say I appreciate you and what you do. Your videos are well explained, very thorough, but also digestible. Some instructors get a bit too technical, or lack enough technicality to fully understand what is happening. Yours are right in the pocket. Thank you, kind sir. You have provided a lot of clarification for me.
I watched many ttorials about jwt because I worked with fullstack php many years and I dont understand rest api so good and This tutorial is where really I did understan about refreshtoken. Thanks
For anyone who doesn't know, if you add secure: true in cookie options, you could only receive cookies on the login route, and in postman couldn't even receive cookies. So test before you add that option.
Thank you Dave. I read through all the comments and your responses to the questions asked which adds more to what has been learned in the video. Thanks again.
Hi Dave, this is by far far one of the best JWT Authentication videos that I have seen on RUclips. I was wondering if you have created a frontend for this project. I am interested to know how one would incorporate the refresh route for a seamless experience if for example, I was on a user page that expired how would I refresh the token and regain access to my session without leaving the page? - many thanks
Great question, Melvyn! And yes, I have very recently. Here is my React Login series that uses the REST API from this Node JS series as the backend: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd 🚀🚀🚀🚀
Firstly, congratulations on your clear, detailed and brilliantly explained tutorials. You have rare communications skills. I am working with Node.js Express and MySQL. I have come across express-session and express-mysql-session that store user credentials server side rather than in the users browser as with JWT. Could you develop tutorials on these packages please?
Dave in the timeframe 34:00 you are checking the founduser name with the decoded username. I feel it is unnecessary because the cookie contains digital signature which can be regenerated by using the header, payload and secret key present only in the server. The verify method would fails if the cookie signature and generated signature doesn't match. what do you mean by tampering.
Thanks for your videos... I learned a lot from your videos. My thunder client didnt work as per your video. After logging in and getting the access token and the refresh token cookie. when i tried to do the /refresh, the cookie wasn't send together. I got empty cookie. When i use postman, its the same case but i can manually add the refresh token cookie into the request and it works. REALLY appreciate your video, one of the best video out there.
When I test the refresh route at 38:45 I'm getting a 401 unauthorized response with no cookies available. I've followed along with the first 40 minutes of this tutorial line by line from scratch several times and the refresh route keeps failing. So I also cloned the repo, installed packages, created the .env file, and I still get stuck testing the refresh route with a 401. Dave is the GOAT of youtube coding tutorials, but this one makes me want to run into traffic. I've sunk in far too many hours on this. MOVING ON.
What confuses me most is how to store and work with the refresh tokens in my database (Postgres). My plan is to allow multi-login from different devices for the same user, so my idea is to create a refresh token for each device the user tries to log in with. Each refresh token belongs to a unique device, so I don't care which user belongs to it. That's why my plan is to create a refresh token table with no user references. When a user logs out, I simply remove the respective refresh token from the database and don't care if they are still logged in on other devices. Interesting notes/tips: 1) If a user deleted their cookie, you would end up with "dead tokens" that never get removed from the database. That only applies to multi-login and shouldn't really be a problem though. 2) Deleting all refresh tokens from the database forces all users to relog-in. Same when you change the refresh token secret. Don't do that, unless you have a good reason. 3) Hashing the refresh tokens before storing them in your database improves security. Similarly how you'd store passwords hashed and not in plaintext. 4) bcrypt stores the salt in the generated hash, so you don't need to store salts in your database. 5) You can check if a refresh token expired by catching "TokenExpiredError". Since it's a valid token (just expired), you can safely call jwt.decode(...) to get the username and other information. Not sure where I would need that, but maybe that helps some of you. Edit: Okay, I just realised that when you compare your refresh token with the database (refresh token hash), you run into the problem of not knowing which salt to use for hashing. You either use the same salt all the time, which makes hashing irrelevant, or you store a user reference for each refresh token hash so you know which entries to look at. Then check each hashed refresh token against the provided refresh token and see if one of them matches... Something feels wrong with that approach though, so for the time being, I'll just save the refresh tokens in plaintext. Edit2: Another reason to store the user id with the refrehs token is that when they change their password, we need to invalidate all existing refresh tokens for that user. Otherwise they stay logged in from their other devices as if they never changed their password. It can also be useful to know all user's refresh tokens if you implement some kind of "logout from all devices" functionality. Generally, you need to store the user id if you want to be able to improve security. Without user id, you'd need to decode all refresh tokens and check the user, which is extremely inefficient.
Excellent content, u are very much unique in teaching different scenarios and I’m so impressed , your channel teaches real-time scenarios, create a Udemy courses will be great 👍 , one question can you do a oauth authentication on node, and when do frontend should call refresh token api
Thank you, Mahendra! I would need to dive deeper into specific docs for OAuth, but with my understanding, I see no reason what Node.js could not do that. To understand when the frontend will call the refresh token, my React login playlist applies all of that knowledge with the Node.js backend you are building in this series: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
21:19 why you set req.user ? In verifyJWT we are just verifying the access token . If it verified then move forward or else error occur. So for what you set req.user to decoded.UserInfo.username?
This is an awesome video Dave. Many thanks to you! I do have a question as well, what if we want to make the API accessible to the public and also enable validation through cookies? The CORS gets in the way
You could make _some_ endpoints open to the public and apply JWT auth to the other endpoints. Concerning CORS - which is not part of the JWT auth - you need to decide upfront if open to the public or not. Usually, a publicly available REST API would be created separately from anything that needed to be secure. You may want to Google to see if anyone has mixed the two - but I don't advise it.
Thank you for the excellent video. I have two questions please: 1- Can we depend only on refresh token without the need of generating the access token? 2- Can we depend only on a session token (without the need of jwt) ?
You can configure different JWT strategies including a one token approach. It is more often the access token only in that case. You can use a session approach instead of JWTs.
Great video as always, one question: why we are not saving Access Token in HTTP only Cookie too? then we don't need to attach it to header in front-end. I'm just curious. is there any security reason? becuase it will make it a lot easier for front-end
Good question! If you are only sending the access token back and not wanting to pull any other data from it, your suggestion could work. If other data is sent inside the access token, JavaScript cannot access a secure httpOnly cookie.
Hey Dave, Thank you for putting this video with great explanation 👌 I have a question, why did you put `req.user = decoded.username` (21:30)? Since we are not using the username from the request in employeesController functions, should we put decoded username in the request? Please let me know if I am missing something
Hello Dave, by the end of the video you showed a frontend app, will you show how to code that in the next videos of this series? Also, are you going to show in the next videos of this series how to use the /refresh route automatically? Because if my understanding is correct, the /refresh route in this video's code will not run automatically, right?
Hi Dave, I have a question, why do I have to save the token to database? How is that helpful? because you are clearing it from the database, when you created the logout middleware. We can just clear the cookies using the clearCookies as you have done and again we are resetting it to empty string. And is this how authentication done in micro service applications? Or do we pass the token in authorization header in order to access the protected routes? PS - Thanks for making these authentication and authorization series. Loving it. 😍😍.
You don't have to save the refresh token to the database. In fact, many do not and say a benefit of JWT is being "stateless". However, if you want to immediately remove access from someone holding a token, you need to track it. I follow this series up with a JWT Rotation and Re-use Detection video where tokens are also tracked. All in all, more than one JWT strategy exists. Your preference on which to apply.
@@DaveGrayTeachesCode Thanks a lot for the suggestion and I will look into that as well. Thanks for making such a good series. I have been struggling with the authentication part for a long time. This is really helpful. Hope you have a good day. 😊
Thank you very much for this awesome tutorial, Dave. I have a question though. We only delete refresh token when user logs out. Does that mean "if some bad guy has access to the user's access token, they still can access protected route for a while before it expires"?
The access token should only be stored in memory (state) on the frontend and should also have a short expiration (15 to 60 minutes - set to preference). In theory though, yes, if someone steals an access token and it is not expired, it will provide access. The undeniable truth: Nothing is 100% secure in the frontend.
Great tutorial, thanks a lot. I hava just one question, why don't you use Passport JS for authentication? I say that because I saw Passport JS is frecuently used for this
HI Dave, I'm getting status 401 when I try to generate refresh token after generating access token. I tried to console.log('request', req.cookies) and it shows [Object: null prototype] {} in terminal.
I needed to modify secure to false in order to have it working in Postman and Thunder Client. res.cookie('jwt', refreshToken, { httpOnly: true, sameSite: 'None', secure: false, maxAge: 24 * 60 * 60 * 1000 }); In comments below there is am answer from Dave "It depends on what you are using to test your endpoints with. In Postman, after you receiving the secure httpOnly cookie with the refresh token, you need to go in and remove the secure: true flag for dev testing. This is because our local dev environment uses http and secure expects https. Or you could just remove the secure: true from the cookie in the code during development - but remember to put it back before deploying."
Fetch and Axios are usually in frontend apps and this video is about Node.js and the backend REST API. I do cover fetch with async / await here: ruclips.net/video/VmQ6dHvnKIM/видео.html
Hy Dave, I'm stuck in verifyJWT controller, I'm not getting authHeader from the req.headers['authorization']. Any help would be much appreciated please
quick question at 45:38 what would happen if the person being iterated through in the database doesnt have a refresh token yet? would the person be filtered through anyway?
@Dave Gray, thks for those videos. It help a lot. However I ve got a question. Why do you put the refresh token in the database ? I've seen lot of tutorial about this subject and I ve Never seen this. Thks for your answer :)
Good question! Yes, many consider a big advantage of JWTs is their "stateless" nature which does not require storage in a database. It is a preference of strategy and can be useful for refresh token re-use detection and rotation as I demonstrate in a later video in this playlist.
@@aurelienchevillotte2874 the approach you use is preference. Some prefer re-use detection as they believe it is more secure - so opinions and approaches vary. If you do not track a token, you cannot immediately remove access - it must expire.
Thank you very much. 39:16 - I wonder why the refresh doesn't work for me, no matter what. When I call the refresh router with GET - The console logs the same cookie again and again (it doesn't change like in your code) And instead of Status: 200 OK, I get - Status: 403 Forbidden. Furthermore, I did look very very careful for a long long time that our codes are similar. (When I copied your folder and ran your completed code from scratch than it is immediately failed with MODULE_NOT_FOUND message)
Hi Louis - I do not have MODULE_NOT_FOUND message here. If you used my code, did you install the npm package dependencies? Those modules are needed to run the code. You should run: npm update ...that will install the npm package dependencies that are listed in my package.json file.
@@DaveGrayTeachesCode Thank you for trying to assist... (1) Regarding my code, after manually copied yours, It is only respond with a "Forbidden" error. What might be the issue? (This is not respond with Status: 200 OK and replacing the cookie "hash number" with every refresh like in your code) (2) Regarding your code (including npm update), logged on the console after running (and crashing) : " Error: secretOrPrivateKey must have a value at Object.module.exports [as sign] (D:\Node-js\express_jwt-main ode_modules\jsonwebtoken\sign.js:107:20) at handleLogin (D:\Node-js\express_jwt-main\controllers\authController.js:21:33) [nodemon] app crashed - waiting for file changes before starting... "
@@shineLouisShine looks like you did not create a .env file that holds the secret keys for your access and refresh token creation. Dot env files are not included in Github repositories so you need to create your own. I believe I show how to do this during the video.
@@DaveGrayTeachesCode Wow, I must admit. this is very frustrating. As to my manually written code - I have no idea what is wrong. I certainly wrote the entire code including "process.env.ACCESS_TOKEN_SECRET". Yet, my code isn't respond as your. As to "copypasting" your code, The server doesn't even get connected, only throw this Error: secretOrPrivateKey must have a value. Plus, obviously, each "env" in the code does appear in its correct position, So I'm not entirely understand your kind instruction. So.... I'm completely lost. Totally, desperately, lost.
(..I don't know how have I missed this segment.. 🤦♂..from 04:45 ) Wow, this is a fascinating deep complicated topic. Thank you for this lesson. There's much much more to read and dig. I must admit, that even after creating the tokens - Yet the refreshing of random cookies one after another such as at your representation - Doesn't work. Should I write anything at the headers fields of Thunder Client? Any key-value pair? Do you have any thought what might be the reason of which it doesn't work yet..?
Hey Dave, thx for this video, it was very informative. I was having issues w/ the JWT middleware causing CORS issues. Turned out when you call the api from the browser (through a react app, for example), it sends 2 requests: an OPTIONS request, followed by the request your front end asked for (GET, POST, etc). In order to fix it, I added a line at the top of my verifyJWT middleware function that returned next() if the req.method was OPTIONS. If anyone else has this issue, hopefully this comment will help them.
Check your CORS middleware and how it is configured. It should be before most everything else in your server and should return the OPTIONS requests well before they get to the verifyJWT middleware.
@@DaveGrayTeachesCode I ended up adding an app.options call to take care of it. I must have missed that part of your node tutorial, since I didn't watch the whole thing. Thanks
Why do we need access token? Can't we use the refresh token itself? I did not understand that at all. Isn't it enough if we extend the validity period of the refresh token and send it in the header of every request and check it in the backend?
hello, when my token is generated and put in cookie, this one is not recovered when I do a refresh which means that I am blocked at this stage... can you help me?
Are their specific microservices you are interested in? A RESTful API like this series has designed powers most microservices. Each microservice then usually has a single function: such as search or other business needs from shipping to payroll.
@@DeepakGupta-hj2dv the integration always depends on the service being created, but the code sending and retrieving data from APIs with fetch or Axios always follows the same pattern.
when we send a get/post request to the protected API we need to set the bearer access token in the cookie so it will be stored in the cookies and vulnerable to XSS or crsf attacks or not like that? what we gained by sending it from the login/refresh API using JSON and storing it in the memory then if we send it back using an authorization bearer token to the API? I'm confused!
What's the purpose of frequently issuing accessToken when we can send accessToken in cookie (http:only) with a defined age and whenever we user want to log out we can clear the cookie from server I mean why to issue refresh token? We can achieve the purpose without it Please explain
We need to issue access tokens frequently because we set them to expire after a short time to improve security of our app. If you don't use refresh tokens, then you should increase life span of access tokens so they don't expire often.
No it is not. In face, many prefer the stateless nature of JWTs. However, I have a tutorial on refresh token rotation and for that, you do need to keep track of tokens.
I can only guess about your code. Please compare to my source code for differences. Source code is available at the course resources link in the description.
Hi Dave, I'm subscribing you from Turkey and really appreciate for your excellent contents. I just want to ask something. When I create the refreshTokenController and add the logic like you did, I'm registering, and having accessToken from /auth route and when I send a request to /refresh route it returns 401 Unauthorized. I thought maybe I missed something you did so I've downloaded the code from your repository. And I'm facing with the same result. What can I do about that? Thanks a lot!
It depends on what you are using to test your endpoints with. In Postman, after you receiving the secure httpOnly cookie with the refresh token, you need to go in and remove the secure: true flag for dev testing. This is because our local dev environment uses http and secure expects https. Or you could just remove the secure: true from the cookie in the code during development - but remember to put it back before deploying.
@@ugurgunes95 you set secure: true in the code when you create the cookie. I can't remember if ThunderClient lets you edit the cookie after you receive it or not. In this series, I remember noting that I removed secure: true for testing with ThunderClient.
That refreshToken thing still confuses me. Lets say realtime, and the access token expires in 15 mins, im an admin and can access all the user information, but when the token expires after 15 mins, it obvi that its gonna be 403 even for the admin. in dev, we could manually send the login creditials again, acquire a new access token, paste it in the bearer section and call for the users info again, it gonna show up eventually. But how will we handle this in real time, how will the access token gonna sit automatically and persist when calling these apis from the front end ?
You can learn all about the frontend strategy and how the refresh token is used to persist a login in my React Auth playlist: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
You have to delete accessToken also on logout because suppose you have given 5 min expiry to the accessToken and then you logout before expired accessToken but in your tutorial you have delete only refreshToken but accessToken still exists so user can still access APIs before expired accessToken. If I'm wrong please correct me
thnks for the video Dave, i had a question what if i want to revoke a user from accessing secure resources from API what would i need to do ? just remove that refresh token of user from DB immediately ?
Yes. If using user roles, remove or disable those, too. A short lived access token - 15 minutes for example - would likely expire soon enough but also applying user roles will allow you to remove access faster.
@@DaveGrayTeachesCode it seems like i need to create another middleware that checks for roles and perform this checking over there , right now its just token verification as you told in the video.
hi dave i am getting this warning "this attemp to set a cookie via set cookie was blocked by user preferance" in incognito mode how can i handle this?? if not handle it will be be fail in incognito mode
This is a Node.js video series which builds the backend. One example of a React frontend that uses this Node.js code as the backend is here: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
Hello dave, may i know why you are storing refresh token inside the database? i don't think we need to store it. also what we should do after the refresh token expires
If an admin wants to remove access before the token expires, the database reference is helpful. Your 2nd question - you need to let the token expire at some point which will require the user to log in again. If not, you are granting indefinite access.
Thank you very much again. 59:20 - Is it possible that after the last changes which have been made to the code - The "GET refersh" call is no loger refresh the cookies, and respond only with "401 Unauthorized"..?
No, everything works for me at that point. Insure your refresh token is set to last long enough that it is not expiring. Are you logging the value of the refresh token when it is received at that endpoint? That will tell you if it is really receiving a value or not. When you have a problem, you have to eliminate each possibility one-by-one until you find the cause before it can be fixed.
After initial user authentication. JWTs can be used for verification at REST API endpoints. In this tutorial, you will learn how to issue access and refresh tokens (JWTs), and also the recommended way to issue these tokens for security concerns. There will also be suggestions for storing these tokens in your frontend apps. If you are just starting out with Node.js and Express, I suggest you start at the beginning of the Node.js for Beginners tutorial series here: ruclips.net/p/PL0Zuz27SZ-6PFkIxaJ6Xx_X46avTM1aYw
So this is how a senior dev works and explains things. Simply amazing. Thank you for these videos.
You're welcome! 💯
Learnt React from Dave and I contributed to one of my friend's project... bro was amazed at the way I did things.... I now feel like a senior dev
I've been following your tutorials on Node, Express, and working my way to Mongo for my project this week. I've told my professor about how helpful your videos are and I've been spreading the word to my classmates. I'm surprised you don't have more views on these tutorials. Thank you for your help!
Thank you, Ali! 🙏🙏
Dude, this is by far the best tutorial series I have seen. Unparalleled quality. Thankyou!
I usually stay away from tutorials but the content you post is insanely good, I have actually learnt a lot from your channel. Since I discovered it my skills with javascript have improved a lot. Thank you so much
Awesome! Thank you! 💯
hey dave this series is really helpful. the way you split things into routers and middleware has been eye-opening. so simple
Glad it helped!
This is masterpiece! 😍 I've thanked on all videos of you I've watched in the comments, and I will do it again on this one because you deserve it. Please keep creating content, I'm seriously worried you might lose interest in it at some point and we lose more content created by you, because you're so good.
Thank you! I'll keep going! 💯🙏
I’m so happy to get my account back with help of this professional teams (name on my channel ) kidney massage him now 😮
be very careful when testing this specific implementation with nodemon vs a normal server. nodemon restarts after any file changes, including changes to models\users.json. This allows each function to get the updated state of refresh token. If you run this without nodemon, your refresh and logout functions will reference old versions of the users.json data that do not include the refresh token from the auth. This causes some unwanted behavior that took me quite a while to catch. As always, thanks @DaveGrayTeachescode for another great and comprehensive lesson
I just wanted to say I appreciate you and what you do. Your videos are well explained, very thorough, but also digestible. Some instructors get a bit too technical, or lack enough technicality to fully understand what is happening. Yours are right in the pocket. Thank you, kind sir. You have provided a lot of clarification for me.
Would love to see an updated version with Typescript and maybe with a relational database. These videos are literally the best.
I watched many ttorials about jwt because I worked with fullstack php many years and I dont understand rest api so good and This tutorial is where really I did understan about refreshtoken. Thanks
Glad it was helpful!
dave calling out whitelist was the perfect endcap to this great video. dave, my man, youve done it again... another fantastic video
Glad you enjoyed it!
For anyone who doesn't know, if you add secure: true in cookie options, you could only receive cookies on the login route, and in postman couldn't even receive cookies. So test before you add that option.
thank god I came across this comment. helped a bunch ahhah thanks
Thank you, i got confused in this part
Thanks it helped. But how do you deal with that in the production enviromment
Wrong, for that You need to set PATH in cookie
Hi Dave, Thanks again for sharing this high quality content. Your explanations are excellent.
You're welcome!
probably the most detailed series on the youtube, although really wish you used ts
Thank you Dave. I read through all the comments and your responses to the questions asked which adds more to what has been learned in the video. Thanks again.
Great to hear! You're welcome!
Thank you for nodejs playlist, Your clear explanations and practical examples made the learning process engaging and effective.
Hi Dave, this is by far far one of the best JWT Authentication videos that I have seen on RUclips. I was wondering if you have created a frontend for this project. I am interested to know how one would incorporate the refresh route for a seamless experience if for example, I was on a user page that expired how would I refresh the token and regain access to my session without leaving the page? - many thanks
Great question, Melvyn! And yes, I have very recently. Here is my React Login series that uses the REST API from this Node JS series as the backend: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd 🚀🚀🚀🚀
@@DaveGrayTeachesCode Fantastic! - thanks again
Thanks Dave, you’re an awesome teacher. Keep it coming please!
Thanks, will do!
Thanks so much, I just finished your node js course so I was going back to some concepts, I don’t thank you enough
You're welcome!
thanks for putting up these videos dave. hopefully the playlist will be a library for node videos that i can come back to if i need a refresher
You're welcome! Glad I could help. 💯
This is a very valuable video for me. I've been trying to understand how the aT and rT works. Thanks very much
Firstly, congratulations on your clear, detailed and brilliantly explained tutorials. You have rare communications skills. I am working with Node.js Express and MySQL. I have come across express-session and express-mysql-session that store user credentials server side rather than in the users browser as with JWT. Could you develop tutorials on these packages please?
I'm a bit late to this back end party, everything works accept I had to use Postman for the refresh and logout routes.
Thanks Dave. 🌮🌮🌮
Dave in the timeframe 34:00 you are checking the founduser name with the decoded username. I feel it is unnecessary because the cookie contains digital signature which can be regenerated by using the header, payload and secret key present only in the server. The verify method would fails if the cookie signature and generated signature doesn't match. what do you mean by tampering.
watched from start of the playlist till this video awesome detailed tutorial, thank you very much
You're very welcome!
Hi Dave. Thank you very much. This series have helped me so much.
Glad I could help! You're welcome! 🙏💯
Again great video. Thank you very much Dave.
Thanks for your videos... I learned a lot from your videos. My thunder client didnt work as per your video. After logging in and getting the access token and the refresh token cookie. when i tried to do the /refresh, the cookie wasn't send together. I got empty cookie. When i use postman, its the same case but i can manually add the refresh token cookie into the request and it works. REALLY appreciate your video, one of the best video out there.
I just finished your next video on the Roles. you did mentioned that secure need to be removed. Thanks !!1
Thanks teacher Dave for making this api totorial availabe,
When I test the refresh route at 38:45 I'm getting a 401 unauthorized response with no cookies available. I've followed along with the first 40 minutes of this tutorial line by line from scratch several times and the refresh route keeps failing. So I also cloned the repo, installed packages, created the .env file, and I still get stuck testing the refresh route with a 401. Dave is the GOAT of youtube coding tutorials, but this one makes me want to run into traffic. I've sunk in far too many hours on this. MOVING ON.
If you set the cookie secure: true, you must use https. Local dev usually just uses http - other comments here on this, too.
Wow. Excellent video. Great job. Thanks a lot for doing these tutorials.
Thank you! 💯
It was a great video, thanks Dave !
Very welcome!
You are awesome. Great videos with a rich content.
Great Explanation 😊
Glad you liked it!
Sir your courses are the best
Thank you!
What a great tutorial. Thank you. I learned a lot
You're welcome! 💯
i have never thought about "whitelist" as something racist
Thanks a lot. You are gifted for teaching.
Thank you!
Wow, very well explained, thank you!
You're welcome!
What confuses me most is how to store and work with the refresh tokens in my database (Postgres). My plan is to allow multi-login from different devices for the same user, so my idea is to create a refresh token for each device the user tries to log in with. Each refresh token belongs to a unique device, so I don't care which user belongs to it. That's why my plan is to create a refresh token table with no user references. When a user logs out, I simply remove the respective refresh token from the database and don't care if they are still logged in on other devices.
Interesting notes/tips:
1) If a user deleted their cookie, you would end up with "dead tokens" that never get removed from the database. That only applies to multi-login and shouldn't really be a problem though.
2) Deleting all refresh tokens from the database forces all users to relog-in. Same when you change the refresh token secret. Don't do that, unless you have a good reason.
3) Hashing the refresh tokens before storing them in your database improves security. Similarly how you'd store passwords hashed and not in plaintext.
4) bcrypt stores the salt in the generated hash, so you don't need to store salts in your database.
5) You can check if a refresh token expired by catching "TokenExpiredError". Since it's a valid token (just expired), you can safely call jwt.decode(...) to get the username and other information. Not sure where I would need that, but maybe that helps some of you.
Edit: Okay, I just realised that when you compare your refresh token with the database (refresh token hash), you run into the problem of not knowing which salt to use for hashing. You either use the same salt all the time, which makes hashing irrelevant, or you store a user reference for each refresh token hash so you know which entries to look at. Then check each hashed refresh token against the provided refresh token and see if one of them matches... Something feels wrong with that approach though, so for the time being, I'll just save the refresh tokens in plaintext.
Edit2: Another reason to store the user id with the refrehs token is that when they change their password, we need to invalidate all existing refresh tokens for that user. Otherwise they stay logged in from their other devices as if they never changed their password. It can also be useful to know all user's refresh tokens if you implement some kind of "logout from all devices" functionality. Generally, you need to store the user id if you want to be able to improve security. Without user id, you'd need to decode all refresh tokens and check the user, which is extremely inefficient.
I suggest joining my Discord for bigger discussions like this one: discord.gg/neKghyefqh
Hey Dave!
Thank you very much .
Welcome!
Thank you so much!
Thanks a lot for your videos! It is really helpful!
You are welcome!
thanks for the tut, quite a good teaching style
You are welcome! 💯
This is great! Thank you so much!
Glad it was helpful! And you're welcome! 💯
Excellent content, u are very much unique in teaching different scenarios and I’m so impressed , your channel teaches real-time scenarios, create a Udemy courses will be great 👍 , one question can you do a oauth authentication on node, and when do frontend should call refresh token api
Thank you, Mahendra! I would need to dive deeper into specific docs for OAuth, but with my understanding, I see no reason what Node.js could not do that. To understand when the frontend will call the refresh token, my React login playlist applies all of that knowledge with the Node.js backend you are building in this series: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
@@DaveGrayTeachesCode thank you so much 😊👍🙌🏻👏🤝
Thank you very much, Dave
You're welcome! 💯
Great explanation Dave ..complete this series? ??
Almost! Now we just need a database technology like Mongo or Postgres. Adding MongoDB will complete the MERN stack when combined with React.
Please start new new project mern stack with react with redux
@@DeepakGupta-hj2dv yes, I will be creating projects with this stack.
You are the best
Thank you! 🙏
21:19 why you set req.user ? In verifyJWT we are just verifying the access token . If it verified then move forward or else error occur. So for what you set req.user to decoded.UserInfo.username?
This is an awesome video Dave. Many thanks to you!
I do have a question as well, what if we want to make the API accessible to the public and also enable validation through cookies?
The CORS gets in the way
You could make _some_ endpoints open to the public and apply JWT auth to the other endpoints. Concerning CORS - which is not part of the JWT auth - you need to decide upfront if open to the public or not. Usually, a publicly available REST API would be created separately from anything that needed to be secure. You may want to Google to see if anyone has mixed the two - but I don't advise it.
Thank you so much for your videos
you are on god mode, sir !!
🙏🙏
Awesome content!
🙏🙏
Big Thanks!!
BIG Welcome!
Just to be clear if the backend and frontend are on the same domain we don't need to set the "Same Site: none".
Excellent clarification!
Thank you for the excellent video. I have two questions please:
1- Can we depend only on refresh token without the need of generating the access token?
2- Can we depend only on a session token (without the need of jwt) ?
You can configure different JWT strategies including a one token approach. It is more often the access token only in that case. You can use a session approach instead of JWTs.
Sir, you are one of the best on the planet. Could you please make a project video on complete MERN stack and posssibly GraphQL ? Thank you
Thank you, TR 🙏 I do have a MERN project in the works. 💯
@@DaveGrayTeachesCode Please update this project with mongoose i.e. MongoDB atlas i.e. Cloud
Amazing tutorial
Thank you, Sona!
Great video as always, one question: why we are not saving Access Token in HTTP only Cookie too? then we don't need to attach it to header in front-end. I'm just curious. is there any security reason? becuase it will make it a lot easier for front-end
Good question! If you are only sending the access token back and not wanting to pull any other data from it, your suggestion could work. If other data is sent inside the access token, JavaScript cannot access a secure httpOnly cookie.
excellent job sir.
Thank you for the kind words! 🙏
Hey Dave,
Thank you for putting this video with great explanation 👌
I have a question, why did you put `req.user = decoded.username` (21:30)? Since we are not using the username from the request in employeesController functions, should we put decoded username in the request?
Please let me know if I am missing something
HI, thanks for video. Great job. Can you tell us about typescript and prisma? thanks.
You're welcome! And thanks for the requests! 🙏
Hello Dave, by the end of the video you showed a frontend app, will you show how to code that in the next videos of this series? Also, are you going to show in the next videos of this series how to use the /refresh route automatically? Because if my understanding is correct, the /refresh route in this video's code will not run automatically, right?
ruclips.net/video/nI8PYZNFtac/видео.html
Hi Dave, I have a question, why do I have to save the token to database? How is that helpful? because you are clearing it from the database, when you created the logout middleware. We can just clear the cookies using the clearCookies as you have done and again we are resetting it to empty string.
And is this how authentication done in micro service applications? Or do we pass the token in authorization header in order to access the protected routes?
PS - Thanks for making these authentication and authorization series. Loving it. 😍😍.
You don't have to save the refresh token to the database. In fact, many do not and say a benefit of JWT is being "stateless". However, if you want to immediately remove access from someone holding a token, you need to track it. I follow this series up with a JWT Rotation and Re-use Detection video where tokens are also tracked. All in all, more than one JWT strategy exists. Your preference on which to apply.
@@DaveGrayTeachesCode Thanks a lot for the suggestion and I will look into that as well. Thanks for making such a good series. I have been struggling with the authentication part for a long time. This is really helpful. Hope you have a good day. 😊
Really great work keep going forward… but if did this with sequelize it’s going to be marvelous… please make lessons on api with sequelize
Thanks for the request! 🙏
Thank you very much for this awesome tutorial, Dave. I have a question though.
We only delete refresh token when user logs out. Does that mean "if some bad guy has access to the user's access token, they still can access protected route for a while before it expires"?
The access token should only be stored in memory (state) on the frontend and should also have a short expiration (15 to 60 minutes - set to preference). In theory though, yes, if someone steals an access token and it is not expired, it will provide access. The undeniable truth: Nothing is 100% secure in the frontend.
Great tutorial, thanks a lot. I hava just one question, why don't you use Passport JS for authentication? I say that because I saw Passport JS is frecuently used for this
Passport JS can be used for auth. Why didn't I use it? It is not the only solution and not what I created the tutorial about.
awesome !!
Thank you! 💯🙏
Excellent tutorial. If you could share the frontend form please.
Thank you! You want this playlist: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
HI Dave, I'm getting status 401 when I try to generate refresh token after generating access token. I tried to console.log('request', req.cookies) and it shows [Object: null prototype] {} in terminal.
I needed to modify secure to false in order to have it working in Postman and Thunder Client.
res.cookie('jwt', refreshToken, { httpOnly: true, sameSite: 'None', secure: false, maxAge: 24 * 60 * 60 * 1000 });
In comments below there is am answer from Dave "It depends on what you are using to test your endpoints with. In Postman, after you receiving the secure httpOnly cookie with the refresh token, you need to go in and remove the secure: true flag for dev testing. This is because our local dev environment uses http and secure expects https. Or you could just remove the secure: true from the cookie in the code during development - but remember to put it back before deploying."
@@ManweyVideos i'm stil struggling with that until now.. i cant resolve /refresh problem with unauthorized 401
Thank you so much. Do you have a starting point for how I can intercept request/response using fetch rather than axios?
Fetch and Axios are usually in frontend apps and this video is about Node.js and the backend REST API. I do cover fetch with async / await here: ruclips.net/video/VmQ6dHvnKIM/видео.html
That DB json file took time and complexity more than using an actual mongodb database.
Good, gooood.
Great content
Thank you, Tejas! 🙏💯
Hy Dave, I'm stuck in verifyJWT controller, I'm not getting authHeader from the req.headers['authorization']. Any help would be much appreciated please
I suggest downloading the source code from the link in the description and comparing to yours to find the differences.
quick question at 45:38 what would happen if the person being iterated through in the database doesnt have a refresh token yet? would the person be filtered through anyway?
@Dave Gray, thks for those videos. It help a lot. However I ve got a question. Why do you put the refresh token in the database ? I've seen lot of tutorial about this subject and I ve Never seen this. Thks for your answer :)
Good question! Yes, many consider a big advantage of JWTs is their "stateless" nature which does not require storage in a database. It is a preference of strategy and can be useful for refresh token re-use detection and rotation as I demonstrate in a later video in this playlist.
@@DaveGrayTeachesCode thanks for your answer. Last question, please, if I don't put refresh token in database does it affect the app security ?
@@aurelienchevillotte2874 the approach you use is preference. Some prefer re-use detection as they believe it is more secure - so opinions and approaches vary. If you do not track a token, you cannot immediately remove access - it must expire.
Thank you very much.
39:16 - I wonder why the refresh doesn't work for me, no matter what.
When I call the refresh router with GET -
The console logs the same cookie again and again (it doesn't change like in your code)
And instead of Status: 200 OK, I get - Status: 403 Forbidden.
Furthermore, I did look very very careful for a long long time that our codes are similar.
(When I copied your folder and ran your completed code from scratch than it is immediately failed with MODULE_NOT_FOUND message)
Hi Louis - I do not have MODULE_NOT_FOUND message here. If you used my code, did you install the npm package dependencies? Those modules are needed to run the code. You should run: npm update ...that will install the npm package dependencies that are listed in my package.json file.
@@DaveGrayTeachesCode Thank you for trying to assist...
(1)
Regarding my code, after manually copied yours,
It is only respond with a "Forbidden" error.
What might be the issue?
(This is not respond with Status: 200 OK and replacing the cookie "hash number" with every refresh like in your code)
(2)
Regarding your code (including npm update), logged on the console after running (and crashing) :
"
Error: secretOrPrivateKey must have a value
at Object.module.exports [as sign] (D:\Node-js\express_jwt-main
ode_modules\jsonwebtoken\sign.js:107:20)
at handleLogin (D:\Node-js\express_jwt-main\controllers\authController.js:21:33)
[nodemon] app crashed - waiting for file changes before starting...
"
@@shineLouisShine looks like you did not create a .env file that holds the secret keys for your access and refresh token creation. Dot env files are not included in Github repositories so you need to create your own. I believe I show how to do this during the video.
@@DaveGrayTeachesCode Wow, I must admit. this is very frustrating.
As to my manually written code - I have no idea what is wrong.
I certainly wrote the entire code including "process.env.ACCESS_TOKEN_SECRET".
Yet, my code isn't respond as your.
As to "copypasting" your code,
The server doesn't even get connected, only throw this Error: secretOrPrivateKey must have a value.
Plus, obviously, each "env" in the code does appear in its correct position,
So I'm not entirely understand your kind instruction.
So.... I'm completely lost.
Totally, desperately, lost.
(..I don't know how have I missed this segment.. 🤦♂..from 04:45 )
Wow, this is a fascinating deep complicated topic.
Thank you for this lesson.
There's much much more to read and dig.
I must admit, that even after creating the tokens -
Yet the refreshing of random cookies one after another such as at your representation -
Doesn't work.
Should I write anything at the headers fields of Thunder Client?
Any key-value pair?
Do you have any thought what might be the reason of which it doesn't work yet..?
I will never understand how people can program code and have it messy like this
Hey Dave, thx for this video, it was very informative. I was having issues w/ the JWT middleware causing CORS issues. Turned out when you call the api from the browser (through a react app, for example), it sends 2 requests: an OPTIONS request, followed by the request your front end asked for (GET, POST, etc).
In order to fix it, I added a line at the top of my verifyJWT middleware function that returned next() if the req.method was OPTIONS.
If anyone else has this issue, hopefully this comment will help them.
Check your CORS middleware and how it is configured. It should be before most everything else in your server and should return the OPTIONS requests well before they get to the verifyJWT middleware.
@@DaveGrayTeachesCode I ended up adding an app.options call to take care of it. I must have missed that part of your node tutorial, since I didn't watch the whole thing. Thanks
Why do we need access token? Can't we use the refresh token itself? I did not understand that at all. Isn't it enough if we extend the validity period of the refresh token and send it in the header of every request and check it in the backend?
Refresh token explanation: auth0.com/docs/secure/tokens/refresh-tokens
Access token explanation: auth0.com/docs/secure/tokens/access-tokens
hello, when my token is generated and put in cookie, this one is not recovered when I do a refresh which means that I am blocked at this stage... can you help me?
Verify that you are sending the secure: true flag in the cookie. As noted in the tutorial, I had to remove it as ThunderClient did not recognize it.
@@DaveGrayTeachesCode , It works, Thank you!
Please make one video Node JS microservices crash course
Are their specific microservices you are interested in? A RESTful API like this series has designed powers most microservices. Each microservice then usually has a single function: such as search or other business needs from shipping to payroll.
@@DaveGrayTeachesCode yes rest api integration microservices series crash course
@@DeepakGupta-hj2dv the integration always depends on the service being created, but the code sending and retrieving data from APIs with fetch or Axios always follows the same pattern.
whats the voice change at 36:50 got me shooked
Hi Dave, jwt launch is successfully...heard that long ago from u
Right on! I'm excited! 💯🚀
when we send a get/post request to the protected API we need to set the bearer access token in the cookie so it will be stored in the cookies and vulnerable to XSS or crsf attacks or not like that? what we gained by sending it from the login/refresh API using JSON and storing it in the memory then if we send it back using an authorization bearer token to the API? I'm confused!
What's the purpose of frequently issuing accessToken when we can send accessToken in cookie (http:only) with a defined age and whenever we user want to log out we can clear the cookie from server
I mean why to issue refresh token? We can achieve the purpose without it
Please explain
We need to issue access tokens frequently because we set them to expire after a short time to improve security of our app. If you don't use refresh tokens, then you should increase life span of access tokens so they don't expire often.
Hello Dave. Is it neccesary to store refresh token in DB? In other tutorial you skipped this step. Thanks in advance for response
No it is not. In face, many prefer the stateless nature of JWTs. However, I have a tutorial on refresh token rotation and for that, you do need to keep track of tokens.
@@DaveGrayTeachesCode BTW thank you for your awesome work! U should be awarded Nobel Prize :D
Hey Dave, thanks for the great tutorial ! I get an undefined req.headers['authorization'] in the verifyJWT middleware. Any hint ?
I can only guess about your code. Please compare to my source code for differences. Source code is available at the course resources link in the description.
43:31 If there's no JWT cookie during logout, shouldn't we send one of 400 codes? Like 401- Unauthorized?
Hi Dave, I'm subscribing you from Turkey and really appreciate for your excellent contents. I just want to ask something. When I create the refreshTokenController and add the logic like you did, I'm registering, and having accessToken from /auth route and when I send a request to /refresh route it returns 401 Unauthorized. I thought maybe I missed something you did so I've downloaded the code from your repository. And I'm facing with the same result. What can I do about that? Thanks a lot!
It depends on what you are using to test your endpoints with. In Postman, after you receiving the secure httpOnly cookie with the refresh token, you need to go in and remove the secure: true flag for dev testing. This is because our local dev environment uses http and secure expects https. Or you could just remove the secure: true from the cookie in the code during development - but remember to put it back before deploying.
@@DaveGrayTeachesCode I am using thunder client since I saw it from your videos. In thunder client I couldn’t find how to set secure: true flag.
@@ugurgunes95 you set secure: true in the code when you create the cookie. I can't remember if ThunderClient lets you edit the cookie after you receive it or not. In this series, I remember noting that I removed secure: true for testing with ThunderClient.
@@DaveGrayTeachesCode I'm going to have some research about it. If I find something I'll let you know. Thanks again :)
The secure: true flag for dev testing works right using Postman but not using Thunder Client 😊
That refreshToken thing still confuses me. Lets say realtime, and the access token expires in 15 mins, im an admin and can access all the user information, but when the token expires after 15 mins, it obvi that its gonna be 403 even for the admin. in dev, we could manually send the login creditials again, acquire a new access token, paste it in the bearer section and call for the users info again, it gonna show up eventually. But how will we handle this in real time, how will the access token gonna sit automatically and persist when calling these apis from the front end ?
You can learn all about the frontend strategy and how the refresh token is used to persist a login in my React Auth playlist: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
You have to delete accessToken also on logout because suppose you have given 5 min expiry to the accessToken and then you logout before expired accessToken but in your tutorial you have delete only refreshToken but accessToken still exists so user can still access APIs before expired accessToken. If I'm wrong please correct me
You certainly can if you want to. Given what should be a short lifespan, you can also just let them expire.
@@DaveGrayTeachesCode OK i understood thanks for your reply
thnks for the video Dave,
i had a question what if i want to revoke a user from accessing secure resources from API what would i need to do ? just remove that refresh token of user from DB immediately ?
Yes. If using user roles, remove or disable those, too. A short lived access token - 15 minutes for example - would likely expire soon enough but also applying user roles will allow you to remove access faster.
@@DaveGrayTeachesCode it seems like i need to create another middleware that checks for roles and perform this checking over there , right now its just token verification as you told in the video.
hi dave i am getting this warning "this attemp to set a cookie via set cookie was blocked by user preferance" in incognito mode how can i handle this?? if not handle it will be be fail in incognito mode
Is there any other video that comes after that? I don't know how to use with front end. 😢
This is a Node.js video series which builds the backend. One example of a React frontend that uses this Node.js code as the backend is here: ruclips.net/p/PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
Hello dave, may i know why you are storing refresh token inside the database?
i don't think we need to store it. also what we should do after the refresh token expires
If an admin wants to remove access before the token expires, the database reference is helpful. Your 2nd question - you need to let the token expire at some point which will require the user to log in again. If not, you are granting indefinite access.
Thank you very much again.
59:20 - Is it possible that after the last changes which have been made to the code -
The "GET refersh" call is no loger refresh the cookies, and respond only with "401 Unauthorized"..?
No, everything works for me at that point. Insure your refresh token is set to last long enough that it is not expiring. Are you logging the value of the refresh token when it is received at that endpoint? That will tell you if it is really receiving a value or not. When you have a problem, you have to eliminate each possibility one-by-one until you find the cause before it can be fixed.