title
React Persistent User Login Authentication with JWT Tokens
description
Web Dev Roadmap for Beginners (Free!): https://bit.ly/DaveGrayWebDevRoadmap
Learn how to add persistent user login authentication with JWT tokens to your React app. We will not store JWT tokens in local storage or session storage. That's not secure! The user login will persist when the React app is refreshed, reloaded, or revisited unless the user logs out or the refresh token has expired.
⭐ Become a full-stack web dev with Zero To Mastery Courses:
- Advanced React: https://bit.ly/AdvReactDev
- Jr to Senior Web Dev Roadmap: https://bit.ly/WebDevRoadmap-JrtoSr
- Master FAANG Coding Interviews: https://bit.ly/FAANGInterview
🚩 Subscribe ➜ https://bit.ly/3nGHmNn
🚀 React JS for Beginners full course - 9 hours: https://youtu.be/RVFAyFWO4go
👀 React Login tutorial series playlist: https://www.youtube.com/playlist?list=PL0Zuz27SZ-6PRCpm9clX0WiBEMB70FWwd
🔗 Starter Source Code: https://github.com/gitdagray/react_jwt_auth
🔗 Completed Source Code: https://github.com/gitdagray/react_persist_login
❗ NOTE: Two updates in source code AFTER video tutorial completed:
1) Added persist to useEffect in PersistLogin component to prevent unwanted call to verifyRefreshToken.
2) RequireAuth component now checks auth.accessToken instead of auth.user to support the persistent login. If you want to leave this as auth.user, just retrieve the user again inside the useRefreshToken hook.
📬 Course Updates ➜ https://courses.davegray.codes/
React Persistent User Login Authentication with JWT Tokens
(00:00) Intro
(00:45) Welcome Discussion
(01:22) Current state of the app
(03:02) Why want a persistent login?
(04:05) PersistLogin component
(10:52) useRefreshToken update
(12:39) Add PersistLogin to App
(14:34) Test the Persistent Login
(16:07) Security Issue #1
(16:42) useLogout hook
(19:47) Add logout to Home
(21:35) Test with no refresh cookie
(22:44) Test with a refresh token
(23:34) Test with an expired refresh token
(26:04) Security Issue #2
(27:13) Adding new state to AuthProvider
(28:54) PersistLogin update
(30:18) Login update
(33:41) Test Trust Device toggle
(35:10) Fix a memory leak
☕ Buy Me A Coffee: https://www.buymeacoffee.com/davegray
The React Login Authentication Series:
1) React Register Form with Validation, Axios and a11y: https://youtu.be/brcHK3P6ChQ
2) React User Login and Authentication with Axios: https://youtu.be/X3qyxo_UTR4
3) React Protected Routes | Role-Based Authorization: https://youtu.be/oUZjO00NkhY
4) React Login Authentication with JWT Access, Refresh Tokens, Cookies and Axios: https://youtu.be/nI8PYZNFtac
5) This video!
🔗 Node JS Full Course (with source code) for building the backend REST API: https://youtu.be/f2EqECiTBL8
🔗 React Router Version 6 in 20 minutes: https://youtu.be/XBRLVRjZ3CQ
🔗 FontAwesome for React:
https://fontawesome.com/v5.15/how-to-use/on-the-web/using-with/react
🔗 RegExr for Regular Expressions: https://regexr.com/
📚 JWT References:
Intro to JSON Web Tokens: https://jwt.io/introduction
All You Need to Know About Storing JWT in the Frontend: https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id
Cross-Site Scripting (XSS): https://owasp.org/www-community/attacks/xss/
Cross-Site Request Forgery (CSRF): https://owasp.org/www-community/attacks/csrf
📚 Accessible Form References:
WebAIM.org - Advanced Forms: https://webaim.org/techniques/forms/advanced
WebAIM.org - Form Validation: https://webaim.org/techniques/formvalidation/
MDN - Aria Attributes:
aria-invalid: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-invalid_attribute
aria-describedby: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby
aria-live: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live
aria-label: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label
🔗 ES7 React JS Snippets Extension for VS Code:
https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets
🔗 React Dev Tools Extension for Chrome:
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
📚 References:
ReactJS Official site: https://reactjs.org/
React Wikipedia: https://en.wikipedia.org/wiki/React_(JavaScript_library)
React Jobs: https://www.ziprecruiter.com/candidate/search?search=react&location=
✅ Follow Me:
Github: https://github.com/gitdagray
Twitter: https://twitter.com/yesdavidgray
LinkedIn: https://www.linkedin.com/in/davidagray/
Blog: https://yesdavidgray.com
Reddit: https://www.reddit.com/user/DaveOnEleven
Was this tutorial about Persistent User Login Authentication with JWT Tokens in React helpful? If so, please share. Let me know your thoughts in the comments.
#react #login #persistent
detail
{'title': 'React Persistent User Login Authentication with JWT Tokens', 'heatmap': [{'end': 406.343, 'start': 379.778, 'weight': 0.735}, {'end': 478.688, 'start': 446.955, 'weight': 0.71}, {'end': 679.404, 'start': 628.576, 'weight': 0.849}, {'end': 786.604, 'start': 737.352, 'weight': 0.781}, {'end': 858.146, 'start': 827.61, 'weight': 0.738}, {'end': 947.915, 'start': 874.617, 'weight': 0.834}, {'end': 1214.315, 'start': 1189.512, 'weight': 0.767}, {'end': 1330.9, 'start': 1302.718, 'weight': 0.713}, {'end': 1779.979, 'start': 1725.545, 'weight': 0.762}], 'summary': 'This tutorial series on react persistent user login authentication with jwt tokens covers adding persistent user login authentication, handling refresh tokens, troubleshooting access token issues, user logout functionality, and managing user authentication and token expiration, providing comprehensive guidance on react component logic, jsx rendering, and state management for secure access and trust features.', 'chapters': [{'end': 417.649, 'segs': [{'end': 166.396, 'src': 'embed', 'start': 137.07, 'weight': 0, 'content': [{'end': 143.535, 'text': 'You do not want to store access tokens in local storage or anywhere else that JavaScript can reach them.', 'start': 137.07, 'duration': 6.465}, {'end': 148.199, 'text': 'Because if you can put it there with JavaScript, a hacker can also retrieve it with JavaScript.', 'start': 143.575, 'duration': 4.624}, {'end': 157.888, 'text': "Now, what you don't see here is that we also get a refresh token, but the backend server issues that in a secure HTTP only cookie.", 'start': 148.52, 'duration': 9.368}, {'end': 160.19, 'text': "And that's where the refresh token is at.", 'start': 158.408, 'duration': 1.782}, {'end': 166.396, 'text': 'And then after a specific set amount of time, what happens is our access token expires.', 'start': 160.43, 'duration': 5.966}], 'summary': 'Store access tokens securely to prevent unauthorized access and token expiration.', 'duration': 29.326, 'max_score': 137.07, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw137070.jpg'}, {'end': 238.314, 'src': 'embed', 'start': 205.238, 'weight': 1, 'content': [{'end': 209.2, 'text': "And either one, what happens is you lose the state and we're logged out.", 'start': 205.238, 'duration': 3.962}, {'end': 213.122, 'text': 'We have to log back in on every refresh or anytime we come back to the app.', 'start': 209.24, 'duration': 3.882}, {'end': 217.084, 'text': 'A persistent user login will eliminate that issue.', 'start': 213.642, 'duration': 3.442}, {'end': 218.925, 'text': "However, it's not a secure.", 'start': 217.384, 'duration': 1.541}, {'end': 227.029, 'text': "So, if you're really trying to make a secure app, I suggest you live with having to log back in and maybe especially, I guess,", 'start': 219.065, 'duration': 7.964}, {'end': 228.57, 'text': 'something financial or otherwise.', 'start': 227.029, 'duration': 1.541}, {'end': 229.87, 'text': 'maybe that would be the way to go.', 'start': 228.57, 'duration': 1.3}, {'end': 238.314, 'text': "But today we're going to add a persistent user login And we'll put a trust this device or remember me check mark down here as well.", 'start': 230.25, 'duration': 8.064}], 'summary': 'Adding a persistent user login for the app to improve user experience and considering security implications for financial transactions.', 'duration': 33.076, 'max_score': 205.238, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw205238.jpg'}, {'end': 406.343, 'src': 'heatmap', 'start': 338.444, 'weight': 2, 'content': [{'end': 342.325, 'text': "We'll set this equal to useState, and we're going to start with a state of true.", 'start': 338.444, 'duration': 3.881}, {'end': 344.665, 'text': "It's going to be a Boolean true or false.", 'start': 342.425, 'duration': 2.24}, {'end': 351.027, 'text': "After that, let's get our refresh function from our useRefreshToken hook.", 'start': 345.266, 'duration': 5.761}, {'end': 359.829, 'text': 'Okay, and then we need to go ahead and grab the current auth from our useAuth hook.', 'start': 352.847, 'duration': 6.982}, {'end': 364.93, 'text': "Let's add a useEffect now.", 'start': 363.57, 'duration': 1.36}, {'end': 372.353, 'text': 'And this useEffect is only going to run when the component loads.', 'start': 369.031, 'duration': 3.322}, {'end': 374.595, 'text': 'So we want an empty dependency array.', 'start': 372.433, 'duration': 2.162}, {'end': 379.638, 'text': "And now inside useEffect, I'll scroll this up so it takes up the rest of the screen.", 'start': 374.615, 'duration': 5.023}, {'end': 382.64, 'text': "Inside the useEffect, we're going to define a function.", 'start': 379.778, 'duration': 2.862}, {'end': 386.202, 'text': "I'm going to call this verifyRefreshToken.", 'start': 382.72, 'duration': 3.482}, {'end': 388.304, 'text': "And it's an async function.", 'start': 387.063, 'duration': 1.241}, {'end': 392.937, 'text': "And inside this function, we're going to have a try block.", 'start': 390.376, 'duration': 2.561}, {'end': 397.839, 'text': "And in the try block, we're just going to await the refresh function.", 'start': 393.437, 'duration': 4.402}, {'end': 402.381, 'text': "That's going to reach out to the endpoint and take the cookie with it.", 'start': 398.319, 'duration': 4.062}, {'end': 406.343, 'text': "And that's the function we created in our use refresh token hook.", 'start': 402.861, 'duration': 3.482}], 'summary': 'Using usestate, useeffect, and async function to verify refresh token.', 'duration': 59.395, 'max_score': 338.444, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw338444.jpg'}], 'start': 0.089, 'title': 'Adding persistent user login in react', 'summary': 'Covers the secure addition of persistent user login authentication to a react app using jwt access and refresh tokens without local or session storage, involving creating components, importing hooks, setting up state, and implementing async functions for token refresh.', 'chapters': [{'end': 228.57, 'start': 0.089, 'title': 'Adding persistent user login in react', 'summary': "Covers adding persistent user login authentication to a react app, securely without using local storage or session storage, which remembers the user's login state and page, leveraging jwt access and refresh tokens for authentication.", 'duration': 228.481, 'highlights': ["The chapter covers adding persistent user login authentication to a React app The tutorial focuses on implementing persistent user login authentication in a React app, ensuring that users' login states are remembered across sessions.", 'Leveraging JWT access and refresh tokens for authentication The tutorial demonstrates the use of JWT access and refresh tokens for secure user authentication, ensuring that the access token is stored in a secure HTTP-only cookie and refreshed when expired.', 'Emphasizing the importance of not storing access tokens in local storage The tutorial highlights the security risks associated with storing access tokens in local storage and recommends storing them in a secure HTTP-only cookie instead to prevent unauthorized access.', 'The tutorial warns about the security implications of persistent user login The tutorial cautions that while persistent user login eliminates the need to log in on every refresh or revisit, it may pose security risks, especially for sensitive applications like financial services.']}, {'end': 417.649, 'start': 228.57, 'title': 'Adding persistent user login in react app', 'summary': 'Explains the process of adding a persistent user login feature to a react application, involving the creation of a new component, importing various hooks, setting up state and implementing async functions for token refresh.', 'duration': 189.079, 'highlights': ['The chapter covers the addition of a persistent user login feature to a React application, requiring the creation of a new component named persistlogin.js and the importation of various hooks such as use state, use effect, use refresh token and use auth.', 'The process involves setting up state for isLoading using useState, retrieving the refresh function from the useRefreshToken hook and obtaining the current auth from the useAuth hook.', 'An async function named verifyRefreshToken is defined within a useEffect, which includes a try block to await the refresh function responsible for obtaining a new access token by sending the cookie to the refresh endpoint.']}], 'duration': 417.56, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw89.jpg', 'highlights': ['The tutorial demonstrates the use of JWT access and refresh tokens for secure user authentication, ensuring that the access token is stored in a secure HTTP-only cookie and refreshed when expired.', 'The tutorial cautions that while persistent user login eliminates the need to log in on every refresh or revisit, it may pose security risks, especially for sensitive applications like financial services.', 'The process involves setting up state for isLoading using useState, retrieving the refresh function from the useRefreshToken hook and obtaining the current auth from the useAuth hook.', 'An async function named verifyRefreshToken is defined within a useEffect, which includes a try block to await the refresh function responsible for obtaining a new access token by sending the cookie to the refresh endpoint.', 'The tutorial highlights the security risks associated with storing access tokens in local storage and recommends storing them in a secure HTTP-only cookie instead to prevent unauthorized access.']}, {'end': 600.139, 'segs': [{'end': 446.475, 'src': 'embed', 'start': 417.649, 'weight': 1, 'content': [{'end': 420.931, 'text': "that would kick us back out, and that's why we've got this component here.", 'start': 417.649, 'duration': 3.282}, {'end': 425.154, 'text': "so here is our catch now and we'll just console.", 'start': 420.931, 'duration': 4.223}, {'end': 434.059, 'text': "let's put console error and pass that in, and then a finally and this finally block is a great place to set.", 'start': 425.154, 'duration': 8.905}, {'end': 436.63, 'text': 'the is loading to false.', 'start': 434.059, 'duration': 2.571}, {'end': 438.091, 'text': 'Spell false.', 'start': 437.371, 'duration': 0.72}, {'end': 438.571, 'text': 'There we go.', 'start': 438.151, 'duration': 0.42}, {'end': 443.513, 'text': 'Because no matter whether we have an error or not, this finally block is always going to run.', 'start': 439.051, 'duration': 4.462}, {'end': 446.475, 'text': 'So that will prevent us from getting in this endless loading loop.', 'start': 443.614, 'duration': 2.861}], 'summary': 'Error handling and loading state management implemented to prevent endless loading loop.', 'duration': 28.826, 'max_score': 417.649, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw417649.jpg'}, {'end': 492.693, 'src': 'heatmap', 'start': 446.955, 'weight': 0, 'content': [{'end': 450.797, 'text': "Now remember, we just defined the function here, so we're still going to need to call that.", 'start': 446.955, 'duration': 3.842}, {'end': 454.799, 'text': "We've also brought in the off state, and that's important.", 'start': 451.317, 'duration': 3.482}, {'end': 464.463, 'text': 'Now, when we come back, say when we would reload the page or come back from another a page back to our app, we will have an empty auth state,', 'start': 454.999, 'duration': 9.464}, {'end': 470.345, 'text': 'and so we need to make sure that we only run this when we do not have that access token.', 'start': 464.463, 'duration': 5.882}, {'end': 475.787, 'text': "we don't want to hit the refresh endpoint just every time we request a protected page.", 'start': 470.345, 'duration': 5.442}, {'end': 478.688, 'text': 'we only want to do it when we lack an access token.', 'start': 475.787, 'duration': 2.901}, {'end': 485.45, 'text': "so here we'll check the auth and i'm going to use optional chaining and check the access token.", 'start': 478.688, 'duration': 6.762}, {'end': 492.693, 'text': "so if we do not have an access token, then we're going to call this function verify refresh token.", 'start': 485.45, 'duration': 7.243}], 'summary': 'Function checks for access token and verifies refresh token if not present.', 'duration': 45.738, 'max_score': 446.955, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw446955.jpg'}, {'end': 533.172, 'src': 'embed', 'start': 509.595, 'weight': 2, 'content': [{'end': 517.001, 'text': "And with that, we have completed the logic that we need for our use effect, but we're not finished with the component itself yet.", 'start': 509.595, 'duration': 7.406}, {'end': 520.544, 'text': "I'm going to add another use effect that we can remove later.", 'start': 517.381, 'duration': 3.163}, {'end': 522.725, 'text': "This is just for us to see what's going on.", 'start': 520.604, 'duration': 2.121}, {'end': 529.349, 'text': "So in this use effect, Once again, it will only run, well, it's not going to run just at load time.", 'start': 522.885, 'duration': 6.464}, {'end': 533.172, 'text': "It's going to run any time the is loading state changes.", 'start': 529.45, 'duration': 3.722}], 'summary': 'Completed logic for use effect, adding another use effect to run when loading state changes.', 'duration': 23.577, 'max_score': 509.595, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw509595.jpg'}, {'end': 600.139, 'src': 'embed', 'start': 576.949, 'weight': 4, 'content': [{'end': 589.17, 'text': "I'll scroll up again and of course that starts with a return and now I'll have a parentheses and we're going to use an HTML fragment And inside the fragment is where we can put some logic here.", 'start': 576.949, 'duration': 12.221}, {'end': 591.592, 'text': "So we'll check our isLoadingState.", 'start': 589.29, 'duration': 2.302}, {'end': 595.515, 'text': "And now let's create a ternary here, and I'll put it on separate lines.", 'start': 592.392, 'duration': 3.123}, {'end': 600.139, 'text': "So the true response would be to say, OK, we're loading.", 'start': 595.595, 'duration': 4.544}], 'summary': 'Using html fragment to handle isloadingstate with ternary logic', 'duration': 23.19, 'max_score': 576.949, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw576949.jpg'}], 'start': 417.649, 'title': 'React component logic and jsx rendering', 'summary': 'Discusses the completion of logic for a use effect, adding another use effect to monitor loading state changes, and implementing jsx rendering with conditional logic.', 'chapters': [{'end': 509.235, 'start': 417.649, 'title': 'Managing loading state and authentication', 'summary': 'Discusses managing the loading state through the finally block and implementing conditional logic to check for the presence of an access token before calling the verify refresh token function.', 'duration': 91.586, 'highlights': ['Implementing conditional logic to check for the presence of an access token The author emphasizes the importance of checking for the existence of an access token before calling the verify refresh token function to avoid unnecessary calls to the refresh endpoint.', 'Managing the loading state through the finally block The finally block is highlighted as a key place to set the loading state to false, ensuring it runs regardless of errors and preventing an endless loading loop.']}, {'end': 600.139, 'start': 509.595, 'title': 'React component logic and jsx rendering', 'summary': 'Covers the completion of logic needed for a use effect, addition of another use effect to monitor loading state changes, and the implementation of jsx rendering with conditional logic.', 'duration': 90.544, 'highlights': ['The chapter covers the completion of logic needed for a use effect, including the use of console logs for monitoring isLoadingState and auth token values.', "An additional use effect is added to monitor loading state changes, providing visibility into the component's behavior.", 'The implementation of JSX rendering with conditional logic, including the use of a ternary operator to handle isLoadingState and display appropriate content.']}], 'duration': 182.49, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw417649.jpg', 'highlights': ['Implementing conditional logic to check for the presence of an access token', 'Managing the loading state through the finally block', 'The chapter covers the completion of logic needed for a use effect, including the use of console logs for monitoring isLoadingState and auth token values', "An additional use effect is added to monitor loading state changes, providing visibility into the component's behavior", 'The implementation of JSX rendering with conditional logic, including the use of a ternary operator to handle isLoadingState and display appropriate content']}, {'end': 886.587, 'segs': [{'end': 679.404, 'src': 'heatmap', 'start': 628.576, 'weight': 0.849, 'content': [{'end': 629.537, 'text': "So let's save this.", 'start': 628.576, 'duration': 0.961}, {'end': 633.278, 'text': 'And then before I forget, we just need our export statement at the bottom.', 'start': 629.777, 'duration': 3.501}, {'end': 640.981, 'text': 'So export default persist login and we should be finished now with our component.', 'start': 633.338, 'duration': 7.643}, {'end': 642.922, 'text': 'so this really handles the logic.', 'start': 640.981, 'duration': 1.941}, {'end': 652.388, 'text': 'to say, if we need to go check that refresh token and that will help our persistent login, only check that refresh token endpoint when it needs to.', 'start': 642.922, 'duration': 9.466}, {'end': 658.551, 'text': "okay, let's go back to the file tree and now i need to go ahead and collapse the components directory.", 'start': 652.388, 'duration': 6.163}, {'end': 664.555, 'text': "but let's look in the hooks directory and find that use refresh token hook and let's quickly look at that.", 'start': 658.551, 'duration': 6.004}, {'end': 667.417, 'text': "i'll hide the file tree again with control b,", 'start': 664.555, 'duration': 2.862}, {'end': 679.404, 'text': "but what we're getting here is an access token when we send our refresh token back to that endpoint And here we're taking the previous state and then we're overriding the access token state.", 'start': 667.417, 'duration': 11.987}], 'summary': 'The component handles logic for refreshing tokens and updating access token state.', 'duration': 50.828, 'max_score': 628.576, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw628576.jpg'}, {'end': 664.555, 'src': 'embed', 'start': 642.922, 'weight': 2, 'content': [{'end': 652.388, 'text': 'to say, if we need to go check that refresh token and that will help our persistent login, only check that refresh token endpoint when it needs to.', 'start': 642.922, 'duration': 9.466}, {'end': 658.551, 'text': "okay, let's go back to the file tree and now i need to go ahead and collapse the components directory.", 'start': 652.388, 'duration': 6.163}, {'end': 664.555, 'text': "but let's look in the hooks directory and find that use refresh token hook and let's quickly look at that.", 'start': 658.551, 'duration': 6.004}], 'summary': 'Improving persistent login by optimizing refresh token endpoint and exploring specific directories for necessary components and hooks.', 'duration': 21.633, 'max_score': 642.922, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw642922.jpg'}, {'end': 790.448, 'src': 'heatmap', 'start': 737.352, 'weight': 0, 'content': [{'end': 742.636, 'text': 'But what I would suggest not doing is do not send back the password that the user has.', 'start': 737.352, 'duration': 5.284}, {'end': 743.917, 'text': "There's no reason for that.", 'start': 742.696, 'duration': 1.221}, {'end': 749.119, 'text': 'OK, just wanted to show you that in the node.js backend code for a second.', 'start': 744.497, 'duration': 4.622}, {'end': 750.88, 'text': "I'll pull that back off the screen.", 'start': 749.239, 'duration': 1.641}, {'end': 758.824, 'text': 'And so now our useRefresh token is receiving both the roles and the access token as needed.', 'start': 751.42, 'duration': 7.404}, {'end': 763.706, 'text': "And without those roles, our user wouldn't be able to access anything because we're using protected routes.", 'start': 759.064, 'duration': 4.642}, {'end': 765.948, 'text': 'And that was in a previous tutorial as well.', 'start': 763.887, 'duration': 2.061}, {'end': 774.173, 'text': "And we're going to see that next because we're going to go back to our app.js and import our persist login component.", 'start': 766.068, 'duration': 8.105}, {'end': 775.194, 'text': "So we'll start here.", 'start': 774.253, 'duration': 0.941}, {'end': 776.695, 'text': "Here's the require auth component.", 'start': 775.294, 'duration': 1.401}, {'end': 778.056, 'text': "Let's put it right underneath that.", 'start': 776.735, 'duration': 1.321}, {'end': 781.459, 'text': 'So import persist login.', 'start': 778.177, 'duration': 3.282}, {'end': 782.36, 'text': 'There we go.', 'start': 781.88, 'duration': 0.48}, {'end': 785.243, 'text': "I'm going to hide the file tree again with Control-B.", 'start': 782.38, 'duration': 2.863}, {'end': 786.604, 'text': 'But scroll down now.', 'start': 785.323, 'duration': 1.281}, {'end': 790.448, 'text': 'And we just want to wrap this around our protected routes.', 'start': 787.625, 'duration': 2.823}], 'summary': 'Node.js backend code enhances user security with protected routes and roles.', 'duration': 64.465, 'max_score': 737.352, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw737352.jpg'}, {'end': 864.75, 'src': 'heatmap', 'start': 827.61, 'weight': 5, 'content': [{'end': 838.799, 'text': "Okay, now I'm just going to highlight the closing route, control X to cut, and scroll down and put this before the catch-all route, so right here.", 'start': 827.61, 'duration': 11.189}, {'end': 840.74, 'text': 'And now I can save.', 'start': 839.52, 'duration': 1.22}, {'end': 843.261, 'text': 'And yes, everything indented as it needs to.', 'start': 841.04, 'duration': 2.221}, {'end': 845.622, 'text': 'So we can see that closing.', 'start': 843.361, 'duration': 2.261}, {'end': 847.883, 'text': 'And now all of these are indented just a little bit.', 'start': 845.842, 'duration': 2.041}, {'end': 850.023, 'text': "And here's the opening with persist login.", 'start': 847.983, 'duration': 2.04}, {'end': 852.704, 'text': "And that's all we really need to do here with the routing.", 'start': 850.323, 'duration': 2.381}, {'end': 858.146, 'text': "Again, in the app.js, when we're using React Router version 6, we don't want logic here.", 'start': 852.824, 'duration': 5.322}, {'end': 864.75, 'text': 'We just want to route and have the components, all the logic should be contained within some of these others.', 'start': 858.246, 'duration': 6.504}], 'summary': 'Demonstration of organizing routes in react router v6 without logic in app.js.', 'duration': 21.389, 'max_score': 827.61, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw827610.jpg'}], 'start': 600.619, 'title': 'Handling refresh tokens and persistent login in react', 'summary': "Discusses handling refresh tokens in a react component, adding user roles to the access token state, and avoiding sending back the user's password. it also outlines implementing persistent login by importing the persist login component and wrapping it around the protected routes to preserve user access after token refresh.", 'chapters': [{'end': 749.119, 'start': 600.619, 'title': 'Handling refresh tokens in react component', 'summary': "Discusses handling refresh tokens in a react component, including the use of a persist login route and the addition of user roles to the access token state, while also highlighting the suggestion to avoid sending back the user's password in the json response.", 'duration': 148.5, 'highlights': ['The chapter discusses handling refresh tokens in a React component, including the use of a persist login route and the addition of user roles to the access token state.', 'The component handles the logic to check the refresh token endpoint when needed, ensuring persistent login.', "A suggestion is made to avoid sending back the user's password in the JSON response from the Node.js backend code.", 'The tutorial mentions the use of a spinner component and the import of the outlet from React Router.']}, {'end': 886.587, 'start': 749.239, 'title': 'Implementing persistent login', 'summary': "Outlines the process of implementing persistent login by importing the persist login component into the app.js file and wrapping it around the protected routes, ensuring that the user's access is preserved even after refreshing the token.", 'duration': 137.348, 'highlights': ['Importing and wrapping the persist login component around protected routes in the app.js file The tutorial demonstrates importing the persist login component and wrapping it around the protected routes in the app.js file to ensure persistent login functionality.', 'Ensuring user access is maintained even after refreshing the token The tutorial emphasizes the importance of maintaining user access even after refreshing the token, highlighting the significance of the persistent login feature.', 'Utilizing React Router version 6 for routing and component logic The chapter underscores the use of React Router version 6 for routing while containing the logic within other components, such as layout and persist components.']}], 'duration': 285.968, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw600619.jpg', 'highlights': ['The tutorial emphasizes the importance of maintaining user access even after refreshing the token, highlighting the significance of the persistent login feature.', 'The chapter discusses handling refresh tokens in a React component, including the use of a persist login route and the addition of user roles to the access token state.', 'The component handles the logic to check the refresh token endpoint when needed, ensuring persistent login.', 'The tutorial demonstrates importing the persist login component and wrapping it around the protected routes in the app.js file to ensure persistent login functionality.', "A suggestion is made to avoid sending back the user's password in the JSON response from the Node.js backend code.", 'The chapter underscores the use of React Router version 6 for routing while containing the logic within other components, such as layout and persist components.']}, {'end': 1161.772, 'segs': [{'end': 935.579, 'src': 'embed', 'start': 886.647, 'weight': 0, 'content': [{'end': 890.029, 'text': 'You could also right-click and choose Inspect, at least on Windows.', 'start': 886.647, 'duration': 3.382}, {'end': 892.164, 'text': "I'll log in Jane.", 'start': 891.164, 'duration': 1}, {'end': 897.826, 'text': "Once she's logged in, we see some things over here in the console.", 'start': 894.745, 'duration': 3.081}, {'end': 905.728, 'text': 'So we started out with is loading is true, and we had our access token, and then is loading is false in our access token.', 'start': 897.946, 'duration': 7.782}, {'end': 906.968, 'text': "And that's really what we wanted.", 'start': 905.768, 'duration': 1.2}, {'end': 909.549, 'text': "And Jane's here at the admin page.", 'start': 907.468, 'duration': 2.081}, {'end': 911.029, 'text': "So I'll go home.", 'start': 909.649, 'duration': 1.38}, {'end': 914.83, 'text': 'We could go to the link page, back to the admin page.', 'start': 911.309, 'duration': 3.521}, {'end': 920.454, 'text': 'It hit a 403 because currently I have the access token expiring in like 10 seconds.', 'start': 915.47, 'duration': 4.984}, {'end': 923.537, 'text': 'just so it would check and hit that 403..', 'start': 920.454, 'duration': 3.083}, {'end': 924.898, 'text': 'We got a new access token.', 'start': 923.537, 'duration': 1.361}, {'end': 926.179, 'text': "Everything's good.", 'start': 925.418, 'duration': 0.761}, {'end': 928.801, 'text': "Let's go ahead and hit refresh, see what happens.", 'start': 926.519, 'duration': 2.282}, {'end': 931.774, 'text': 'Yep Our page is still here.', 'start': 929.581, 'duration': 2.193}, {'end': 933.436, 'text': 'Jane was able to log back in.', 'start': 931.874, 'duration': 1.562}, {'end': 935.579, 'text': "Let's see if we got some information up above.", 'start': 933.476, 'duration': 2.103}], 'summary': 'Tested user login functionality, including token expiration and refresh.', 'duration': 48.932, 'max_score': 886.647, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw886647.jpg'}, {'end': 1048.729, 'src': 'embed', 'start': 1020.232, 'weight': 2, 'content': [{'end': 1023.677, 'text': "However, here we're going to get the refresh token from the cookie.", 'start': 1020.232, 'duration': 3.445}, {'end': 1026.862, 'text': 'So we need to send the cookie back when we hit this log out endpoint.', 'start': 1023.757, 'duration': 3.105}, {'end': 1031.204, 'text': 'And then down here further in the code, you can see we clear the cookie.', 'start': 1027.603, 'duration': 3.601}, {'end': 1039.586, 'text': "This deletes the cookie altogether, and that will keep Jane or any user from being logged back in when they don't expect it.", 'start': 1031.444, 'duration': 8.142}, {'end': 1048.729, 'text': "So we need to actually manually apply a way to log out, and that way they won't be logged in until that refresh token expires, which, again,", 'start': 1039.705, 'duration': 9.024}], 'summary': 'Implementing a manual logout process to prevent unexpected logins and ensure refresh token expiry.', 'duration': 28.497, 'max_score': 1020.232, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1020232.jpg'}, {'end': 1091.183, 'src': 'embed', 'start': 1060.452, 'weight': 3, 'content': [{'end': 1063.313, 'text': "So I'll highlight the hooks directory, create a new file.", 'start': 1060.452, 'duration': 2.861}, {'end': 1065.374, 'text': "And we'll call this use logout.", 'start': 1063.413, 'duration': 1.961}, {'end': 1071.315, 'text': "I'm going to make this a hook because we might want to use this logout in more than one place in the app in the future.", 'start': 1065.434, 'duration': 5.881}, {'end': 1073.956, 'text': "But I'm only going to put it in one place today.", 'start': 1071.895, 'duration': 2.061}, {'end': 1076.857, 'text': "OK, we'll import Axios.", 'start': 1074.496, 'duration': 2.361}, {'end': 1083.54, 'text': "But instead of just being from Axios, this is from an instance we've already created in our API folder.", 'start': 1077.557, 'duration': 5.983}, {'end': 1086.501, 'text': 'And this will just be the Axios instance.', 'start': 1084.44, 'duration': 2.061}, {'end': 1087.101, 'text': 'There we go.', 'start': 1086.541, 'duration': 0.56}, {'end': 1091.183, 'text': "And after that, we're going to need the useAuth hook again.", 'start': 1087.862, 'duration': 3.321}], 'summary': "Creating a 'use logout' hook for potential future use, utilizing axios instance and the useauth hook.", 'duration': 30.731, 'max_score': 1060.452, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1060452.jpg'}], 'start': 886.647, 'title': 'Troubleshooting access token and user logout functionality', 'summary': 'Covers troubleshooting access token expiration causing a 403 error, leading to the generation of a new access token for successful page loading. it also explains the implementation of user logout functionality to clear the cookie and prevent unexpected logins, ensuring secure access and preventing unauthorized access.', 'chapters': [{'end': 958.543, 'start': 886.647, 'title': 'Troubleshooting access token and page loading', 'summary': 'Details the troubleshooting of access token expiration causing a 403 error, leading to the generation of a new access token, and successful page loading, with the access token and loading status being monitored in the console.', 'duration': 71.896, 'highlights': ['Access token expiration caused a 403 error The access token expired, resulting in a 403 error due to the short 10-second expiration window.', 'Generation of a new access token A new access token was generated, resolving the 403 error and enabling successful page loading.', 'Monitoring loading status and access token in the console The loading status and access token were observed in the console, with the initial values showing loading as true and the access token as undefined, followed by their updates to false and the actual token, respectively.']}, {'end': 1161.772, 'start': 958.543, 'title': 'Implementing user logout functionality', 'summary': "Explains the need to implement a user logout functionality to clear the cookie and prevent unexpected logins, as well as creating a 'uselogout' hook to facilitate the logout process, ensuring secure access and preventing unauthorized access.", 'duration': 203.229, 'highlights': ['The need to implement a user logout functionality to clear the cookie and prevent unexpected logins is emphasized. The importance of implementing user logout functionality to clear the cookie and prevent unexpected logins is highlighted for maintaining user security and preventing unauthorized access.', "The creation of a 'useLogout' hook is described to facilitate the logout process and ensure secure access. The 'useLogout' hook is created to facilitate the logout process and ensure secure access, enhancing the user experience and preventing unauthorized access."]}], 'duration': 275.125, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw886647.jpg', 'highlights': ['Generation of a new access token A new access token was generated, resolving the 403 error and enabling successful page loading.', 'Access token expiration caused a 403 error The access token expired, resulting in a 403 error due to the short 10-second expiration window.', 'The need to implement a user logout functionality to clear the cookie and prevent unexpected logins is emphasized. The importance of implementing user logout functionality to clear the cookie and prevent unexpected logins is highlighted for maintaining user security and preventing unauthorized access.', "The creation of a 'useLogout' hook is described to facilitate the logout process and ensure secure access. The 'useLogout' hook is created to facilitate the logout process and ensure secure access, enhancing the user experience and preventing unauthorized access.", 'Monitoring loading status and access token in the console The loading status and access token were observed in the console, with the initial values showing loading as true and the access token as undefined, followed by their updates to false and the actual token, respectively.']}, {'end': 1618.44, 'segs': [{'end': 1227.453, 'src': 'heatmap', 'start': 1189.512, 'weight': 0.767, 'content': [{'end': 1194.595, 'text': "So now we need to import this where we're going to use it, and that is back in our home component.", 'start': 1189.512, 'duration': 5.083}, {'end': 1199.199, 'text': "Let's go back to the component directory and scroll down to home.", 'start': 1194.635, 'duration': 4.564}, {'end': 1203.044, 'text': "so we really haven't looked at this component in a couple of tutorials here.", 'start': 1199.199, 'duration': 3.845}, {'end': 1209.55, 'text': 'so we were still pulling in our auth directly from the context and not using use auth at that point.', 'start': 1203.044, 'duration': 6.506}, {'end': 1211.232, 'text': "but we don't even need use auth.", 'start': 1209.55, 'duration': 1.682}, {'end': 1214.315, 'text': "now we're going to set the auth inside of use logout.", 'start': 1211.232, 'duration': 3.083}, {'end': 1227.453, 'text': "so i just deleted both of those imports and here we'll just import use And after that we will get rid of this setAuth that we're pulling in from the auth context as well.", 'start': 1214.315, 'duration': 13.138}], 'summary': "Updating component to use 'useauth' instead of 'auth context'.", 'duration': 37.941, 'max_score': 1189.512, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1189512.jpg'}, {'end': 1288.049, 'src': 'embed', 'start': 1262.435, 'weight': 0, 'content': [{'end': 1268.462, 'text': 'Now we need to apply signout where we were calling logout down in the JSX with our buttons.', 'start': 1262.435, 'duration': 6.027}, {'end': 1270.064, 'text': "So let's scroll down to the bottom.", 'start': 1268.542, 'duration': 1.522}, {'end': 1274.018, 'text': 'and put sign out there instead of log out.', 'start': 1271.596, 'duration': 2.422}, {'end': 1283.325, 'text': "So now we have updated our sign out button and we're actually going to make a request to that endpoint, the log out endpoint, in our backend,", 'start': 1274.418, 'duration': 8.907}, {'end': 1284.706, 'text': 'Node.js. REST API.', 'start': 1283.325, 'duration': 1.381}, {'end': 1288.049, 'text': "It's going to delete the cookie that has the refresh token.", 'start': 1284.746, 'duration': 3.303}], 'summary': "Replace 'logout' with 'signout' in jsx. request to logout endpoint in node.js rest api deletes refresh token.", 'duration': 25.614, 'max_score': 1262.435, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1262435.jpg'}, {'end': 1330.9, 'src': 'heatmap', 'start': 1302.718, 'weight': 0.713, 'content': [{'end': 1309.622, 'text': "I'll go ahead and hit the console or Control-Shift-I to open the DevTools console.", 'start': 1302.718, 'duration': 6.904}, {'end': 1311.103, 'text': "I'll clear that out as well.", 'start': 1309.642, 'duration': 1.461}, {'end': 1312.504, 'text': "Let's go to the login.", 'start': 1311.523, 'duration': 0.981}, {'end': 1315.665, 'text': "OK, and now I'm going to get rid of login in the URL.", 'start': 1312.524, 'duration': 3.141}, {'end': 1318.527, 'text': "So we're going to the home page, which is a protected page.", 'start': 1315.685, 'duration': 2.842}, {'end': 1321.889, 'text': "Let's see what we get in the console and see what happens.", 'start': 1318.607, 'duration': 3.282}, {'end': 1330.9, 'text': "okay, we were directed back to the login as expected, and this is what happens when we don't have a cookie, and seeing red over here is okay,", 'start': 1322.469, 'duration': 8.431}], 'summary': 'Tested login process, redirected to login without cookie.', 'duration': 28.182, 'max_score': 1302.718, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1302718.jpg'}, {'end': 1489.859, 'src': 'embed', 'start': 1455.086, 'weight': 2, 'content': [{'end': 1458.387, 'text': 'because we want to see the result when the refresh token is expired.', 'start': 1455.086, 'duration': 3.301}, {'end': 1462.407, 'text': 'So we have a cookie, but the token inside the cookie is expired.', 'start': 1458.447, 'duration': 3.96}, {'end': 1464.068, 'text': "So now let's try that out.", 'start': 1462.827, 'duration': 1.241}, {'end': 1474.629, 'text': "I'll pull this back off the screen and we'll come back over here and we're going to need to log out, totally signed out, clear everything out there.", 'start': 1464.128, 'duration': 10.501}, {'end': 1479.13, 'text': "And now we're going to need to log in and get a token.", 'start': 1475.469, 'duration': 3.661}, {'end': 1480.47, 'text': "So here's the login.", 'start': 1479.39, 'duration': 1.08}, {'end': 1481.13, 'text': 'There we go.', 'start': 1480.67, 'duration': 0.46}, {'end': 1489.859, 'text': "So here we go with Jane and she's logged in now and she gets a refresh token.", 'start': 1481.971, 'duration': 7.888}], 'summary': 'Testing refresh token expiration with login and token retrieval', 'duration': 34.773, 'max_score': 1455.086, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1455086.jpg'}, {'end': 1559.73, 'src': 'embed', 'start': 1531.775, 'weight': 4, 'content': [{'end': 1537.138, 'text': 'otherwise the user sees the same result no refresh token is valid.', 'start': 1531.775, 'duration': 5.363}, {'end': 1540.46, 'text': "so they're kicked back out here to the login, and that's great.", 'start': 1537.138, 'duration': 3.322}, {'end': 1541.56, 'text': "so everything's working.", 'start': 1540.46, 'duration': 1.1}, {'end': 1549.945, 'text': "when jane's logged in and has a valid refresh token, it works, and if jane is logged in and has an expired refresh token, it doesn't work.", 'start': 1541.56, 'duration': 8.385}, {'end': 1559.73, 'text': "I'm going to pull the Node.js code back over here quickly and I'm going to change that 15 seconds back to one day for now and save because we're finished checking with that.", 'start': 1550.325, 'duration': 9.405}], 'summary': "System works with valid refresh token; doesn't work with expired one.", 'duration': 27.955, 'max_score': 1531.775, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1531775.jpg'}], 'start': 1162.293, 'title': 'User authentication and token management', 'summary': 'Covers implementing a user logout function in a react application, using uselogout hook to handle the logout process and updating the sign out button to make a request to the backend node.js rest api. it also describes the process of user authentication and refresh token usage, including scenarios of login, token expiration, and trust this device feature, showcasing the functionality of access and refresh tokens, and the impact of token expiration on user access to the system.', 'chapters': [{'end': 1312.504, 'start': 1162.293, 'title': 'Implementing user logout function', 'summary': 'Covers implementing a user logout function in a react application, using uselogout hook to handle the logout process and updating the sign out button to make a request to the backend node.js rest api, effectively deleting the refresh token cookie for improved security.', 'duration': 150.211, 'highlights': ['Implementing the useLogout hook to handle the logout process, which sets the auth to an empty object and makes a request to the backend Node.js REST API to delete the refresh token cookie, enhancing security.', 'Updating the sign out button in the JSX to invoke the sign out function instead of the previous logout function, ensuring that users can manually log out and have the refresh token deleted from the backend, thus improving security.', 'Utilizing the useLogout hook, removing unnecessary imports, and applying the signout function to the button in the JSX, allowing for manual logout and enhanced security measures.']}, {'end': 1618.44, 'start': 1312.524, 'title': 'User authentication and refresh token', 'summary': 'Describes the process of user authentication and refresh token usage, including scenarios of login, token expiration, and trust this device feature, showcasing the functionality of access and refresh tokens, and the impact of token expiration on user access to the system.', 'duration': 305.916, 'highlights': ['The chapter details the process of user authentication and refresh token usage, including scenarios of login, token expiration, and trust this device feature. The chapter covers various scenarios of user login, token expiration, and the trust this device feature, offering insights into the process of user authentication and refresh token usage.', 'The impact of token expiration on user access to the system is demonstrated through examples of access and refresh token functionality. The chapter showcases the impact of token expiration on user access to the system, providing examples of access and refresh token functionality in scenarios of token expiration and its consequences on user access.', 'The process of user authentication and refresh token usage is detailed, highlighting the functionality of access and refresh tokens in various user login scenarios. The chapter delves into the process of user authentication and refresh token usage, emphasizing the functionality of access and refresh tokens in different user login scenarios, providing a comprehensive understanding of token usage.']}], 'duration': 456.147, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1162293.jpg', 'highlights': ['Implementing the useLogout hook to handle the logout process and delete the refresh token cookie from the backend Node.js REST API, enhancing security.', 'Updating the sign out button in the JSX to invoke the sign out function, ensuring manual logout and improved security measures.', 'Detailing the process of user authentication and refresh token usage, covering scenarios of login, token expiration, and trust this device feature.', 'Showcasing the impact of token expiration on user access to the system, providing examples of access and refresh token functionality.', 'Emphasizing the functionality of access and refresh tokens in different user login scenarios, providing a comprehensive understanding of token usage.']}, {'end': 2245.249, 'segs': [{'end': 1687.495, 'src': 'embed', 'start': 1641.748, 'weight': 0, 'content': [{'end': 1647.19, 'text': "So click on that and currently we're just sending the auth and set auth in the auth provider.", 'start': 1641.748, 'duration': 5.442}, {'end': 1655.657, 'text': "But now we need to add some more state So now let's go ahead and say const, and we're going to have not an object, but another array.", 'start': 1647.55, 'duration': 8.107}, {'end': 1656.097, 'text': 'There we go.', 'start': 1655.677, 'duration': 0.42}, {'end': 1659, 'text': 'Persist and set persist.', 'start': 1656.157, 'duration': 2.843}, {'end': 1664.785, 'text': "And I'm going to set that equal to use state, but I'm going to want to expand this.", 'start': 1660.021, 'duration': 4.764}, {'end': 1667.187, 'text': 'So control B to hide the file tree again.', 'start': 1664.865, 'duration': 2.322}, {'end': 1669.389, 'text': "And now I'm going to say json.reset.", 'start': 1667.688, 'duration': 1.701}, {'end': 1674.111, 'text': "parse and inside of this I'm going to have local storage.", 'start': 1670.45, 'duration': 3.661}, {'end': 1680.873, 'text': "Now earlier today for this tutorial I said we wouldn't use local storage for access tokens or refresh tokens and we're not.", 'start': 1674.431, 'duration': 6.442}, {'end': 1687.495, 'text': "This is only going to hold a boolean to say whether we trust this device or not and that's just fine for local storage.", 'start': 1681.253, 'duration': 6.242}], 'summary': 'Adding more state using an array, not local storage for tokens.', 'duration': 45.747, 'max_score': 1641.748, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1641748.jpg'}, {'end': 1779.979, 'src': 'heatmap', 'start': 1725.545, 'weight': 0.762, 'content': [{'end': 1731.63, 'text': "so those will be available as well and we'll be able to pull those directly from use auth, just like we are auth and set auth,", 'start': 1725.545, 'duration': 6.085}, {'end': 1734.392, 'text': 'because it has the auth context in it.', 'start': 1731.63, 'duration': 2.762}, {'end': 1735.333, 'text': 'with that complete,', 'start': 1734.392, 'duration': 0.941}, {'end': 1746.401, 'text': "let's go back to the file tree and inside of the file tree i'm going to open up the components directory and go back to the persist login component we've created today.", 'start': 1735.333, 'duration': 11.068}, {'end': 1756.247, 'text': "so now we're pulling in auth from use auth, so we also need to grab persist here, And with persist we're going to apply some logic in our JSX.", 'start': 1746.401, 'duration': 9.846}, {'end': 1761.349, 'text': 'So this was already applying a little bit of logic based on is loading.', 'start': 1756.707, 'duration': 4.642}, {'end': 1766.492, 'text': 'But now persist is essentially going to say whether we need to do this check at all.', 'start': 1761.79, 'duration': 4.702}, {'end': 1770.234, 'text': "So if we don't need to do this check, we just want to show the outlet.", 'start': 1766.772, 'duration': 3.462}, {'end': 1779.979, 'text': "So let's say, if we do not have persist and i'll create an extra line here, because this will be a chain ternary, but it's not a big, complicated one.", 'start': 1770.314, 'duration': 9.665}], 'summary': 'Integrating use auth and persist into components for logic and display.', 'duration': 54.434, 'max_score': 1725.545, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1725545.jpg'}, {'end': 2239.945, 'src': 'embed', 'start': 2210.332, 'weight': 2, 'content': [{'end': 2213.993, 'text': "We don't have a memory leak because we fixed that inside of that component.", 'start': 2210.332, 'duration': 3.661}, {'end': 2223.876, 'text': 'OK, I hope this tutorial has shown you how to add a persistent login without using local storage for the access tokens,', 'start': 2214.433, 'duration': 9.443}, {'end': 2228.037, 'text': 'but still use it in your application when you want to trust this device.', 'start': 2223.876, 'duration': 4.161}, {'end': 2234.721, 'text': 'Remember to keep striving for progress over perfection, and a little progress every day will go a very long way.', 'start': 2228.297, 'duration': 6.424}, {'end': 2239.945, 'text': "Please give this video a like if it's helped you, and thank you for watching and subscribing.", 'start': 2235.382, 'duration': 4.563}], 'summary': 'Tutorial on adding persistent login without local storage, striving for progress over perfection.', 'duration': 29.613, 'max_score': 2210.332, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw2210332.jpg'}], 'start': 1618.44, 'title': 'Adding state and persistent login', 'summary': 'Covers adding state to the auth provider context using an array, setting persist, and utilizing local storage for device trust. it also demonstrates adding persistent login functionality using local storage, discusses access token implications, and emphasizes focusing on progress over perfection.', 'chapters': [{'end': 1687.495, 'start': 1618.44, 'title': 'Adding state to auth provider', 'summary': 'Covers the process of adding state to the auth provider context in visual studio code using an array for persist and set persist, while utilizing local storage to store a boolean value for device trust.', 'duration': 69.055, 'highlights': ['Adding state using an array for persist and set persist in the auth provider context in Visual Studio Code.', 'Utilizing local storage to store a boolean value to indicate device trust.']}, {'end': 2245.249, 'start': 1687.935, 'title': 'Adding persistent login with local storage', 'summary': 'Demonstrates the process of adding persistent login functionality using local storage and demonstrates the implementation through code snippets. it also discusses the implication of using local storage for access tokens and provides a reminder to focus on progress over perfection.', 'duration': 557.314, 'highlights': ['The chapter demonstrates the process of adding persistent login functionality using local storage The tutorial provides a detailed walkthrough of adding persistent login functionality using local storage, demonstrating the implementation through code snippets.', 'The implementation through code snippets The tutorial includes code snippets that showcase the step-by-step implementation of persistent login functionality using local storage.', 'Implication of using local storage for access tokens The tutorial discusses the implication of using local storage for access tokens, highlighting the practicality and implications of this approach.', 'Reminder to focus on progress over perfection The tutorial concludes with a reminder to prioritize progress over perfection and emphasizes the significance of consistent improvement in coding practices.']}], 'duration': 626.809, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/27KeYk-5vJw/pics/27KeYk-5vJw1618440.jpg', 'highlights': ['Adding state using an array for persist and set persist in the auth provider context in Visual Studio Code.', 'Utilizing local storage to store a boolean value to indicate device trust.', 'The chapter demonstrates the process of adding persistent login functionality using local storage.', 'The tutorial provides a detailed walkthrough of adding persistent login functionality using local storage, demonstrating the implementation through code snippets.', 'The tutorial includes code snippets that showcase the step-by-step implementation of persistent login functionality using local storage.', 'The tutorial discusses the implication of using local storage for access tokens, highlighting the practicality and implications of this approach.', 'The tutorial concludes with a reminder to prioritize progress over perfection and emphasizes the significance of consistent improvement in coding practices.']}], 'highlights': ['The tutorial demonstrates the use of JWT access and refresh tokens for secure user authentication, ensuring that the access token is stored in a secure HTTP-only cookie and refreshed when expired.', 'The tutorial emphasizes the importance of maintaining user access even after refreshing the token, highlighting the significance of the persistent login feature.', 'Implementing the useLogout hook to handle the logout process and delete the refresh token cookie from the backend Node.js REST API, enhancing security.', 'The need to implement a user logout functionality to clear the cookie and prevent unexpected logins is emphasized. The importance of implementing user logout functionality to clear the cookie and prevent unexpected logins is highlighted for maintaining user security and preventing unauthorized access.', 'The chapter discusses handling refresh tokens in a React component, including the use of a persist login route and the addition of user roles to the access token state.']}