Refresh Token Rotation and Reuse Detection in Node.js JWT Authentication

preview_player
Показать описание


(00:00) Intro
(00:12) Welcome
(00:40) Refresh Token Rotation Explained
(02:26) Multiple Device / Login Support
(04:03) Refresh Token Reuse Detection
(11:08) Refresh Controller
(16:50) Logout Controller
(17:46) Auth Controller
(21:09) Verify logout deletes refresh token
(22:31) Delete an old token at login
(24:46) Identify an expired refresh token
(27:09) Confirm refresh token reuse detection
(30:59) Check multi device / login support
(32:32) Last Minute Addition: An important scenario

TLDR: Nothing in the browser is 100% completely secure. We just secure it as best we can.

📚 Refresh Token Rotation and Reuse Detection References:

📚 JWT References:

✅ Follow Me:

#refresh #token #rotation
Рекомендации по теме
Комментарии
Автор

Brilliant tutorial Dave! I added to this by offering a 'logout of all devices' option to my user account page and simply called a backend route that set the relevant user's refreshToken array to an empty array.

kevl
Автор

Hi Dave! Please consider this scenario :
User A login using Device A = Acces Token(A) = OK, Refresh Token(A) = OK
User A login using Device B = Acces Token(B) = OK, Refresh Token(B) = OK
User A requests tokens using Device A = Acces Token(C) = OK, Refresh Token(C) = OK
User B tries to access resources using the expired Refresh Token (A) --> Reuse Detection --> All Refresh Token Deleted (OK)

Here is the loop beginning:

User A requests tokens using Device A by Using Refresh Token C (Deleted) --> Reuse Detection --> All Refresh Token Deleted (OK)
User A login using Device A = Acces Token(D) = OK, Refresh Token(D) = OK
User A requests tokens using Device B by Using Refresh Token B (Deleted) --> Reuse Detection --> All Refresh Token Deleted (OK)
and so on... Reuse Detection Loop

RahdixCloudNine
Автор

Thank you very much for this excellent tutorial, and of course, for the linked source code, which helps me understand everything at my own pace ! <3

NicolasRoehm
Автор

WOW! Not that many videos i will watch few times but this one definitely.

jovan
Автор

Simply one of the best tutorials on youtube

jessbk
Автор

woah I thought the jwt part is done but there's more. thanks again Dave, currently watching I hope I can follow along implement this in my app.

willyhorizont
Автор

This is gold! Good job, DG! Happy new year!!!

pinwanrj
Автор

Thank you so much for this helpful playlist! I watched all the videos, and they provided me with valuable insights and knowledge that I needed. Great content!

kfksunb
Автор

Thank you very much, you are the best! real-life technique in the tutorial your series it's GOLD

micaeldie
Автор

You deserve all the medals my good ser, if you create any courses I will surely buy or donate some.

You are tackling important aspects that is very useful for any project in very exquisite detail.

dindoleonard
Автор

Hey Dave! Your tutorials are awesome! You're a great teacher. Just to know, Are you planning a nextjs series with the newest features? Again congratulations for such an amazing job. Greetings from Argentina!

Frankitoact
Автор

You are one of the best teachers out there. You explain things like a professor. Did you do education as a major in school?

co_bby
Автор

Awesome +++ You are one of the best teachers out there

mohamedyoussef
Автор

Hi Dave, Thanks for your awesome tutorials. I found your tutorials really explicit. I learned a lot things from your tutorials specially on JWT.

By the way just to be clear, in refresh controller section @ 12.36 minute, condition in if statement should be if(err && user.id === decoded.id) instead of If(err) or condition in second if statement (err && user.id !== decoded.id) instead of (err || user.id !== decoded.id).

as if(err) and (err || user.id !== decoded.id) evaluates the same, if I am not wrong on this point.

shibleesaidul
Автор

Thanks again Dave! This series keep geting better and better.
One question: Doesn't these changes create a situation where the time limit on the refesh token is extended more or less indefinitely (if continuously refreshed)?
Maybe this is acceptable or maybe should the new refresh token keep the expire date of the original? Am I overthinking this? Any thoughts?

henrikskarbrandt
Автор

Hi Dave, thank you for the excellent React and Node youtube series on refresh token and refresh token rotation. Your series is fantastic! They are the best and most in-depth I have seen on Youtube and Udemy on JWT authentication.

I have a question, and I apologise for the long post in advance.

Based on your tutorial,

1) Upon the expiry of the initial auth token (at 1.0), the backend will check if the refresh token (rt 1.0) appears in the refreshToken[] (in the userSchema). If it appears in the refreshToken[], the backend will then generate a new auth token (at 1.1) in json + refresh token (rt 1.1) via httpOnly cookie and return to the frontend
2) The refresh token will always have the same expiry (e.g. one day) after it is re-issued.
3) Once the refresh token is re-issued, the backend will remove the expired refresh token (rt 1.0) from the refreshToken[] and add the new refresh token (rt 1.1) into refreshToken[] for the next verification.
5) If a hacker uses an old refresh token to authenticate (i.e. valid refresh token but does not appear in refreshToken[]), the backend will empty refreshToken[]. This will force all logins (belonging to this user) on other devices to re-login.
6) If there is no hacker and the user does not validate with the new auth token (at 1.1), the new refresh token (rt 1.1) will expire based on the expiresIn key in the jwt.sign() method


Am I correct to say that a potential vulnerability exists when a hacker gets hold of the refresh token (rt 1.1) and refreshes it to rt 1.2 upwards (before rt 1.1 expires), AND the valid user does not use the (rt 1.1) to authenticate (triggering an empty array in the refreshToken[])?

E.g. A valid user (employee) who last refreshes the refresh token (rt 2.0) on Friday at 7 pm, and the refresh token is valid for 24 hours. As long as the hacker gets the refresh token (rt 2.0) and refreshes it (to rt 2.1 onwards) before Sat 7 pm (assuming no work over the weekend), the hacker will have valid access until the user try to auth with the token (rt 2.0).

In fact, if the machine is stolen/harddisk formatted, the valid user will not be able to authenticate (with rt 2.0). Hence, the hacker will get infinite access by renewing the refresh token (rt 2.1 upwards) before its expiry.

I have thought of 3 potential enhancements that would reduce (not remove) the risk, and I would love to have your input.

1) Implement a log out everywhere button where the backend will set an empty array to the refreshToken[] (in the userSchema). User will have to clear all cookies in all other logged-in devices and log in again (to prevent an endless loop of the cookie, as mentioned by Glacial in the previous posts)

2) The refresh token will have a shorter expiry time (e.g. 1 hr), so the refresh token will be invalid 1 hour after no authentication is made. This leaves a smaller window for a hacker to have a valid refresh token

3) All subsequence refresh tokens will have a shorter expiry time than the initial refresh tokens. For example
-> backend generates initial refresh token (rt 1.0) at 1 pm with an expiry of 2 hrs
-> Backend re-generates refresh token (rt 1.1) at 1.10 pm with an expiry of 1hr 50 mins
-> Backend re-generates refresh token (rt 1.2) at 1.17 pm with an expiry of 1hr 43 mins
This will require more coding. We need to store the expiry time (e.g. 2 pm) in the jwt payload, calculate the remaining time to expiry time, and set it as the expiresIn in the next refresh token. This ensures that the hacker will not be able to renew the refresh token indefinitely even if the log out everywhere button is not pressed.

Let me know what you think, and thank you for the awesome tutorials!

WongYamPan
Автор

Thank you Dave ! It works great, but I need to understand a little thing.

At the login, if there is no cookie with the value of the current refresh token, the tutorial updates the database by keeping the old refresh tokens and adding the one that has just been created at login.

Thus, for each manual deletion of cookies, another refresh token is added to database. (5 manual deletion before login = 5 refresh tokens in database.

What I need to understand is, do you think there is a reason to stock all of them ?

jejepro
Автор

Great video, very comprehensive !!!

authController takes care of the Login process.
1) Why don't you include the accessToken in the cookies, but rather in body?
2) It seems that the code does not handle the scenario where a user logins with a valid refreshToken belongs to a different user. The new accessToken array is not affected, but would it be a concern?

logoutController - concerning the scenario where refreshToken is invalid. (refreshToken is not in db).
How should we handle this case, only by sending 204?

nirgluzman
Автор

Amazing series Dave! I already finished the React course and decided to check on this one to apply these concepts in my Python backend. I just have one quick question. Do you happen to know how the sessions would be cleared out from the database? Let's say the user uses a lot of "Incognito" mode in chrome to log into our app. This will create a Refresh Token each time they log out from a new Incognito window, or device. Eventually, their Refresh Token array in the DB will be filled out with a lot of sessions that will be all expired at some point. Would it be a good practice to have some sort of "cleaner" system in the backend to check and clear the expired Refresh Tokens? or Is there a better way to do it?

Thank you!

tecneto
Автор

I like refactoring when it results in a more robust app,
It's always easier to improve your app functionalities when your code is well structured,

for multi-device/user testing and unless it's a browser compatibility issue, I tend to use Chrome's Incognito window when I only need one additional user,
If another user is needed I use Chrome's guest profile, more than that, I add additional profiles (without linking them to accounts),

Thanks Dave,

ahmad-murery