JWT Authentication | Persist Login State on Refresh | MERN Stack
Вставка
- Опубліковано 4 лип 2024
- Web Dev Roadmap for Beginners (Free!): bit.ly/DaveGrayWebDevRoadmap
Learn JWT Authentication and how to persist login state on refresh in this MERN Stack Project tutorial. You will learn how to use JWT access tokens and refresh tokens in React with RTK Query and Redux as well as persist your app state when you refresh the page.
Note: JWT access tokens are sent in an authorization header, but most viewers search for "JWT Authentication" which explains this title instead of "JWT Authorization".
💖 Support me on Patreon ➜ / davegray
⭐ Become a full-stack web dev with Zero To Mastery Courses:
- Advanced React & Redux: bit.ly/AdvReactDev
- The Complete Node.js Developer: bit.ly/CompleteNodeJS
- Jr to Senior Web Dev Roadmap: bit.ly/WebDevRoadmap-JrtoSr
🚩 Subscribe ➜ bit.ly/3nGHmNn
📬 Course Updates ➜ courses.davegray.codes/
🚀 Discord ➜ / discord
☕ Buy Me A Coffee ➜ www.buymeacoffee.com/davegray
👇 Follow Me On Social Media:
Github: github.com/gitdagray
Twitter: / yesdavidgray
LinkedIn: / davidagray
🔗 All Resources for this MERN Stack Project: github.com/gitdagray/mern_sta...
🔗 Playlist for this MERN Stack Project Series: bit.ly/3Sn4EaI
JWT Authentication | Persist Login State on Refresh
(00:00) Intro
(00:18) Welcome
(00:35) Starter Code & User Stories
(01:58) Send access token with a baseQuery
(05:10) Test the baseQuery requests
(07:51) baseQueryWithReauth wrapper function
(13:12) Test the baseQueryWithReauth requests
(15:55) Fix for useQuery when it doesn't unsubscribe
(22:12) usePersist custom hook
(23:36) Add Persist toggle to Login form
(25:24) Quick CSS addition
(25:52) Persist Login component
(36:43) Set credentials with onQueryStarted
(38:09) Add PersistLogin to App.js
(39:04) Test the Persistent Login State
Corrections:
(16:39) Terminology 'labels' vs 'cache entries': When different values are placed here, different caches are created. In the upcoming 'Refactor' chapter 12 video, we insure all queries use the same cache entry value to avoid creating additional caches.
📚 Suggested Pre-requisites for this MERN course:
🔗 Node.js for Beginners full course: • Node.js Full Course fo...
🔗 React JS for Beginners full course: • React JS Full Course f...
🔗 Redux Toolkit for Beginners full course: • React Redux Full Cours...
🔗 React Login Playlist: • React Login, Registrat...
📚 Tutorial References:
🔗 Redux Devtools: github.com/reduxjs/redux-devt...
🔗 Redux Toolkit Docs: Extending fetchBaseQuery for Authorization: redux-toolkit.js.org/rtk-quer...
Was this JWT Authentication and Persist Login State on refresh tutorial helpful? If so, please share. Let me know your thoughts in the comments.
#jwt #authentication #refresh
Part 10 of The MERN Stack Project Series - In this lesson, we utilize both JWT access tokens and refresh tokens for continuous authorization to our app data. We also set up a persistent login component that allows us to remain logged in even after a page refresh. This tutorial is not for beginners. If you are a beginner, check out my full courses all in one playlist here: ua-cam.com/play/PL0Zuz27SZ-6M1Uopt6_VL3gf3cpMnwavm.html
😍 thanks for such a great work
I list recommended prerequisite courses before attempting this MERN Stack Project series in the description of the video and discuss them in the first lesson of the series. I recommend reviewing those recommended courses and then coming back to this series.
Thanks for this useful tutorial Mr. Dave, everytime I watch your videos I just gain motivation to keep up developing and learning with you! Greetings from Turkey.
Glad to hear that! And hello to Turkey! 👋
Your classes are the best. Done with your CSS and reactjs classes, now on node. Thanks a ton
Great to hear! Keep making progress! 💯🚀
Thank you very much, you're one of the best teachers and coders on UA-cam. You're a pro coder, your content helped me a lot. God bless you, Dave!!!
You're very welcome! 💯🙏
Thank you Dave! This is masterpiece, even if my brain got melted after watching it though. I need to come back to this few times to fully understand it :)
Right on! 🚀
saving all your videos so i can binge them all at once, this is great
That works! 💯
I really love your explanations. I have learnt a lot from your tutorial series, keep creating such amazing tutorials.
Happy to hear that! 💯
Hi Dave! RTK Query author here.
At 16:47 you use query arguments as "labels" to distinguish them from preloaded data - the problem with that is that you create a whole unique cache entry - your preloaded data will now be completely meaningless as it is preloaded and then pretty much tossed away, never to be used. And that new cache entry will make a new request to the server.
You should probably not be doing that - data for the same request should always be in the same cache entry.
Query arguments are not labels, query arguments should be data that leads to different requests being made in the `query` function.
It would be great if you could correct this in a comment or an annotation to the video.
I found the spot you are referring to at 16:39. I will note this as requested and my terminology of 'label' instead of 'cache entry' may leave something to be desired. You could say the 'cache entry' is the 'label' we see for the cache in Redux Devtools. Please note that preloaded data approach is changed in the appropriately titled "Refactor" chapter of the series in just a couple of videos where a prefetch strategy is applied instead and all requests use the same 'cache entry' including the prefetch requests so no additional entries are created: ( ua-cam.com/video/jEVyPJ3U_y0/v-deo.html ) I will make a 'label' to 'cache entry' annotation that also indicates a refactor is still upcoming before the series ends. Thank you for the input! 💯
this has been very helpful. I am glad I know your channel. my project is coming together because of your videos
Great to hear!
Another amazing tutorial as usual!
Thank you! Cheers!
Mindblowing lesson. So many useful information.
Glad it was helpful!
You are excellent teacher!!!!
great video Dave thank you for your hard work
Very welcome!
Foy anyone who might have missed it, you need to set the cookie as secure: false on the authController on the backend to create the users using postman or thunder client, but once you begin testing the refreshToken on the react front-end, you need to switch the cookie to secure: true, otherwise it won't work while testing the app.
Good note!
really helpful, thanks!
You literally just saved me hours of debugging. Thank you
So excited for this one!!
Glad to hear that, Kiera! 💯
This is quality content. Thank you sir.
I'm glad it is helpful! 💯🚀
Very nice series you got here..But I've read RTK documentation essentials quite well from chapter 1 to 8 and I can't find most of all you been doing in videos 9 and 10 as regards authentication.please where can I find them
Hi Dave, love using your tutorials, I have been using this as a base for my site for a while, one thing I have found is that my refresh works fine on desktop, (chrome) however on my Iphone, it does not, (Safari or Chrome). Is this an iphone/mobile related issue, or is there a way we can work around this, thank you!
Thank you very much for these valuable sharing
You're welcome!
Like always - TOP!!! Thank You, so much!!!💪
Thank you!
Hi, if i have two backends one for authentication and another one for CRUD. and each backend has IP. so how can i deal with the baseQuery ?
because am planing to have auth server (SSO) and that will be connected to other applicatoins that i will build.
Thanks
Hello Dave, thank you very much for the content! Kindly let me know how I can implement access token on page reload as the state gets cleared. If user does not check the box for “trust device” then refresh token will not be used and access token will be empty on page reload. Thank you very much in advance.
Hi Dave, I really appreciate your content so far, so easy to learn and also very useful.
by the way, i have a question, on my current app, I'm trying to store the access token in the cookie on my front end, I'm using react-cookies. how can I pass down the cookie value into RTK API slice?
Thank you! If you get the access token value (to store in a cookie or store in Redux), you can then dispatch an action creator to store the token. For example, in this MERN Stack tutorial, I dispatch the setCredentials action creator to store the access token in Redux state.
@@DaveGrayTeachesCode thank you for the explanation. really appreciate it! wish u all the best
Hi Dave, thanks sharing such a good knowledge.
We use redux-persist if the application needs a persist every state? If just a simple persist actually we can just build it ourself just like this tutorial component?
Good question, but no, I do not suggest using redux-persist for auth state. The redux-persist package uses localStorage. You do not want to keep secure auth data there. For other data, it is fine.
@@DaveGrayTeachesCode great. Thanks for your guiding.
Hey Dave !
great content as usual !
please, could you make a course about chrome developer tool for dummies ?
Thank you! And great request! 💯
Hi Dave, amazing tutorial. But I have a query, is it ok to use redux persist package to persist the state? Is it fine as what I saw is the package store all the information of the state in local storage itself. And on refreshing the page it reset the redux state from the local storage.
It's okay to do that but don't do it with any sensitive information because local storage is not secure.
thank you so much for your videos, i am really learning.Please i want to ask,with regards to the login persist,can redux persist be used as well, cos i saw yours is safer, reason being that it is not in the local storage.can redux persist be used without the local storage?🙌
You're welcome, Miles. redux-persist uses localStorage. I would not use it to store auth data.
@@DaveGrayTeachesCode thank you for your reply.you are very correct.your videos taught me so much I am very grateful.much Respect Sir.
hey dave, i got a question. How to restrict access to login and register when the user is logged in?
Hey Dave I have a question. I ran into an issue that happens when the user refreshes too many times too fast or when requests are being made too fast. Basically what happens is the initial refresh request happens on the server. The server deletes the current refresh token from the database and begins to make the new on. But if the user issues another request before getting an updated token, its sends the same token again, which triggers the detected refresh token reuse code. Thus requiring the user to login again. In react, how can I modify axiosPrivate to hold of on requests when a refresh token is currently being updated. I've tried to fix this with many different solutions, not of which worked. Thanks and amazing tutorial by the way!
You can apply a rate limiter. Several available at npmjs.com
Given how verbose RTK Query is, this is really good work, top notch content.
Thank you, Marvin! 💯
Amazing tutorial thank you
You're very welcome!
Hello thanks for this course . beside the great quality of your content another thing that is really important about your videos is the length of them , your videos are not short and just about fundamentals afterward I want to ask about mvc architecture are they fully implemented in the node js projects or mern stack projects as well as other frame works? for example larval or .net core ? and what is your opinion about typescript? should we use them in our node projects? and what about deno frame work? sorry for lots of question 🙏
Thank you for the kind words! 🙏
Q1) MVC architecture is a choice that can be implemented, and I do use it in the backend of this MERN Stack Project. It is very common and used with other backend stacks, too.
Q2) Typescript is great. It is a developer tool. It makes your code easier for other devs to understand. I can see why so many teams are choosing to use it.
Q3) I don't usually use Typescript for small / personal projects as it just adds to dev time. That said, it is good to learn it, and I understand why dev teams want to use it.
Q4) Lots of great frameworks out there for frontend and backend. It is good to know at least one for each well - like Node.js and React - and then know some about others.
@@DaveGrayTeachesCode thank you for the answer and your time
Thanks a lot Dave I learned a lot from you I wish you all the best, I learned Nestjs and I liked, can you put any Nestjs videos?
Great suggestion! I want to cover Nestjs, too!
First of all, thank you very much!
Second, is "PreFetch" component considered a bad practice? for a large amount of data we would like to to pull data in sections (for example 10 documents per page) am i right?
You're welcome! 💯
Good question - if that is what you want, you could set up your backend endpoint to provide paginated results with 10 docs per response. More on pagination here: ua-cam.com/video/9ZbdwL5NSuQ/v-deo.html ...also, while I am just showing one Prefetch component as needed by this application - you could create more than one for different parts of an application and only wrap them around the components that need that data much like a context provider.
@@DaveGrayTeachesCode Great thanks! :)
I'm using the newest version of RTK and adding a setTimeout() around apiSlice.util.resetApiState() no longer solves the subscription issue. Please reply if you had the same issue so I know it's not just my code.
Just confirming that when using the newest version of @reduxjs/toolkit and react-redux, the logout will not navigate you back to the public page. Using the version Dave uses here will get you expected behavior.
Great tutorial Dave! Really learning alot. I do have one problem, Everything was working fine until 40:30 where you reload the page, the login stays persisted. For me when I reload I get "Unauthorized" coming from the verifyJWT. I looked at your git repo to make sure everything is same but still running into this problem. Any solution to fix this?
actually figured it out, in our refresh action creator for onQueryStarted I had "await queryFulfilled()" instead of "await queryFuilfilled" which was stoping the refresh token from being retrieved from the cookies..
Good work!
Hey Dave everytime I log in my application persist defaults to "Token and Uninit", however in your app it defaults to "Success". I have tried rewatching the videos but can't find any mistake I am making, could you kindly guide me. I appreciate the work you've put out in the content and channel thank you in advance.
Worth noting I downgraded to the versions you were working with and it worked.
thanks Dave 😇
Welcome!
I don’t know why! After I apply this logic for persist login to my capstone project which is an E-commerce website. The developer team from Korea, they says this is not a standard way to persist the login and they want me to do research more on how to persist the login.
There are many different approaches you can research. This is only one.
Thanks!
Thank you for the support! 🙏💯
Great !!!!
Thanks!
Awesome👌👌👌
Thanks!
On refresh, login is not getting persisted. I followed everything to the T. what am I doing wrong?
There must be something different. When a problem occurs, many students tell me they did everything *exactly* the way I did, but we usually find a small difference somewhere.
Thank you!
You're welcome!
Great.
How many parts are coming?
We are near the end. I can estimate 2 more from here, but I never set a specific number. I just go where the content leads and make as many as necessary.
this is exactly what i want
Right on!
everything is perfect but when access token expires and refresh token mechanism is triggered, query runs twice after fetching the new access token.
I don't know what made it run twice. it is OK with GET request. but POST request adds double entry to the database.
7:51 so I was just doing this just yesterday hours before this video came out. I was implementing this section here to add a reauth wrapper around the base fetchQuery function, using typescript. An issue I ran into was a circular dependency issue. It was being caused by using the same actions in the wrapper as those defined in the authSlice, i.e. setCredentials in this video's case. If this error happens to you, you'd need to define the actions you'll use inside the reauth wrapper and authSlice in a separate file and import from there.
What do I mean by this? I'll use the code in the video to explain
Let's say you implement this code line for line but ran into a circular dependency error from rtk query. What you need to do is remove setCredentials (and other actions being called in there for your case) from the reauth wrapper and from the authSlice. Create a new file and define those actions again, i.e.
export const setCredentials=createAction("setCredentials)
Import the action(s), setCredentials in this case, into your authSlice and use it as an extra reducer using the builder.addCase syntax. The payload for that action being what you're expecting from the reauth if it succeeds. Then import setCredentials again in the file you defined your reauth wrapper and use it accordingly.
Hope this helps because it took me a while to break the circular dependency lol
The code in this tutorial does not result in a circular dependency, but thank you for sharing as it might help someone who modifies the code and has one. 💯
@@DaveGrayTeachesCode no problem, I believe some won't run into this issue at all. I actually ran into the issue from when I was referencing one of your videos actually 😂. I didn't know what I was doing wrong or how differently I did everything, and for you everything is working fine but for me it wasn't. So I sought out how to break the dependency loop and now I'm sharing it here.
Hello Dave, thank you for this amazing tutorial, i have error , refresh token is not working, i thought i have a mistake in the code, but when i clone your repo and test it, it is not working as well, please can you help me.
The error occur when i log in then click to notes list, and when the token expire the refresh wont work, and i get error ( unauthorized )
You cannot simply clone the backend code and run it. You need to create your own token secrets and save them as environment variables. I show how to do this in a previous lesson from this series. Environment variables (.env files) are never included in a Github repository.
@@DaveGrayTeachesCodeYes, I did create my own secret key and database. I do not know what am doing wrong. everything works fine, but the refresh is not, I get unauthorized when I try to get the refresh token. anyway thanks a lot, I learned a lot of things from your videos, and I'll try to figure it out
@@BkDev23 I have the same issue, could you solve it?
Sir can i ask why mine is not getting real time request when my token expired , i need to reload the page to know if the token is expired
I do not specifically know about _yours_ ..I can only provide what is in the video and suggest you compare your code to the completed code I have provided. If something is not working as I have shown, there must be a difference in your code somewhere. You can use a tool like www.diffchecker.com/ to compare.
@@DaveGrayTeachesCode thank you sir
#1 comment,1st Viewer
when I add your new code, I'm getting blocked by CORS
I cover CORS back in the middleware lesson - I think lesson 2.
@@DaveGrayTeachesCode its working just fine until I add the new code. I have been following your series
There must be a difference in there somewhere as I am not receiving a CORS error with this new code.
sir how can i stop OPTIONS request?
You don't stop OPTIONS requests, but you can learn more about them and CORS here: ua-cam.com/users/shorts1iOeoRCUD4M
@@DaveGrayTeachesCode why I having this type of request and your tutorial video has not?
@@germaneswalter8560 I do have options requests happening in my tutorial. In the prerequisite Node.js I explain how to handle them with your CORS settings. The prerequisite course was listed in lesson 1 and is also recommended in the course resources for this series.
@@DaveGrayTeachesCode thank you for your response sir Dave, anyway I use mysql
Friend first of all congratulations for your FANTASTIC VIDEOS, you explain very well
Unfortunately, my English is bad and the translations are difficult for me to understand... I am generally very visual... there is a way to make an image where the whole process is graphed... it would be like a flow chart... ... I hope you like the idea and can do it
Greetings Jose Grillo from Venezuela
TRADUCCION
Amigo ante todo felicitaciones por sus FANTASTICOS VIDEOS, esplicas muy bien
Lamerntablemente mi mal ingles y la traduciones me cuesta un poco entender.... YO por lo general soy muy visual.... abra forma de hacer una imagen donde se grafique todo el proceso... seria como un diagrama de flujo..... Espero que le guste la idea y lo pueda hacer
Te saluda Jose Grillo de Venezuela
Thank you for the kind words, Jose! I have noted your request and will consider making something like this. 💯
@@DaveGrayTeachesCode
wow, it would be super..... with that I guarantee that it would be very useful for those of us who are just starting out, at least for me it would be; It would be an additional PLUS to your fantastic work... God bless you;
You deserve more subscribers; I DECREE that you will soon reach 100,000, then 1,000,000 and many more....
ESPAÑOL
waoo, seria super..... con eso garantizo que seria de gran utilidad para los que estamos empezando, por lo menos para mi lo seria; Seria un PLUS adicional a tu fantastico trabajo.... Dios te bendiga;
Mereces mas subscriptores; DECRETO que pronto llegaras a 100.000, luego al 1.000.000 y muchos mas....
Your tutorial is advance. It is little bit difficult for the Junior developers to understand. Of course watching many times can clear the doubt
Yes, this is lesson 10 in the series. In lesson 1, I list the pre-requisites and state this is an advanced series.
If you have problem with cross site cookie on Chrome since v80, cannot attach cookie on localhost and cannot reauthenticate using refresh token. Please improve configuration cookie on backend with this.
const secure.= false
res.cookie('jwt', refreshToken, {
httpOnly: true,
secure,
sameSite: secure ? ''none : 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000,
})
change sameSite: 'none' to sameSite: 'lax' will solve this isssue. samesite none must attach secure to true run on https only
When deployed, you do want secure: true, but in dev mode, localhost is http and not https so secure must be set to false for dev.
@@DaveGrayTeachesCode yeah, you right
Thanks dude you save my hours
Hello, @DaveGrayTeachesCode I am trying to do the persist login with NextJS and I am having challenges with how to go about it cos on page refresh, the token from redux clears. I would appreciate any tips you can give me.
I cover how to persist the auth after a page refresh in this MERN series. You may not have watched that part yet.
@@DaveGrayTeachesCode I have rewatched this whole series but everytime I refresh my page the token disappears the refresh function is never called and I have no idea what I'm doing wrong please help me troubleshoot this.
I dont know why but your configuration of useState inside usePersist custom hook returns false for me when i reload the page ...What worked for me was this:
useState(JSON.parse(localStorage.getItem("persist")) ? true : false)
Anyways thanks for the tutorial I learned a lot from this one
{2023-03-25}, {2023-03-30}