title
Python Flask Tutorial: Full-Featured Web App Part 10 - Email and Password Reset

description
In this Python Flask Tutorial, we will be learning how to send emails to reset a user's password. Users will be able to fill out a form with their email and have a unique token sent to them, and if their token is verified then they will be able to create a new password. We will be using the flask-mail and itsdangerous packages to help us with this. Let's get started... The code for this series can be found at: https://github.com/CoreyMSchafer/code_snippets/tree/master/Python/Flask_Blog Environment Variables (Windows): https://youtu.be/IolxqkL7cD8 Environment Variables (Mac and Linux): https://youtu.be/5iWhQWVXosU ✅ Support My Channel Through Patreon: https://www.patreon.com/coreyms ✅ Become a Channel Member: https://www.youtube.com/channel/UCCezIgC97PvUuR4_gbFUs5g/join ✅ One-Time Contribution Through PayPal: https://goo.gl/649HFY ✅ Cryptocurrency Donations: Bitcoin Wallet - 3MPH8oY2EAgbLVy7RBMinwcBntggi7qeG3 Ethereum Wallet - 0x151649418616068fB46C3598083817101d3bCD33 Litecoin Wallet - MPvEBY5fxGkmPQgocfJbxP6EmTo5UUXMot ✅ Corey's Public Amazon Wishlist http://a.co/inIyro1 ✅ Equipment I Use and Books I Recommend: https://www.amazon.com/shop/coreyschafer ▶️ You Can Find Me On: My Website - http://coreyms.com/ My Second Channel - https://www.youtube.com/c/coreymschafer Facebook - https://www.facebook.com/CoreyMSchafer Twitter - https://twitter.com/CoreyMSchafer Instagram - https://www.instagram.com/coreymschafer/ #Python #Flask

detail
{'title': 'Python Flask Tutorial: Full-Featured Web App Part 10 - Email and Password Reset', 'heatmap': [{'end': 1496.248, 'start': 1465.642, 'weight': 0.717}, {'end': 1650.082, 'start': 1618.398, 'weight': 0.704}, {'end': 1772.886, 'start': 1667.586, 'weight': 1}, {'end': 1978.964, 'start': 1920.415, 'weight': 0.736}, {'end': 2079.455, 'start': 2021.557, 'weight': 0.846}, {'end': 2255.286, 'start': 2223.533, 'weight': 0.871}], 'summary': 'Tutorial series covers token generation and validation, user password reset process, creating password reset routes, and setting up password reset and email configuration in flask, with details on expiration times, token verification, form creation, route handling, and email configuration.', 'chapters': [{'end': 215.383, 'segs': [{'end': 96.605, 'src': 'embed', 'start': 20.004, 'weight': 0, 'content': [{'end': 30.146, 'text': "So first let's look at how we can generate a secure time sensitive token that we can use to make sure that only someone who has access to the user's email can reset their password.", 'start': 20.004, 'duration': 10.142}, {'end': 36.227, 'text': "So to do this, we'll be using the package called it's dangerous, which should have been installed when we installed flask.", 'start': 30.466, 'duration': 5.761}, {'end': 39.808, 'text': "So let's open up our command line and see how this is going to work.", 'start': 36.567, 'duration': 3.241}, {'end': 45.109, 'text': "So I'm going to open a Python terminal here, and now I'm going to do a long import here.", 'start': 40.128, 'duration': 4.981}, {'end': 49.23, 'text': "So I'm going to say from it's dangerous, and that's all one word.", 'start': 45.129, 'duration': 4.101}, {'end': 59.593, 'text': 'import, and this import is timed JSON web signature and you have to get this camel casing correctly as well.', 'start': 49.81, 'duration': 9.783}, {'end': 66.454, 'text': "serializer and we'll import that just simply as serializer.", 'start': 59.593, 'duration': 6.861}, {'end': 69.035, 'text': "so if I spelled that correctly, then we shouldn't get any errors.", 'start': 66.454, 'duration': 2.581}, {'end': 75.737, 'text': "And now we're going to use this serializer and pass in a secret key and an expiration time in seconds.", 'start': 69.555, 'duration': 6.182}, {'end': 85.081, 'text': 'So to do that, I can say s is equal to serializer, and we will pass in a secret key of just secret for now.', 'start': 76.078, 'duration': 9.003}, {'end': 89.562, 'text': 'and then a time, expiration time of 30 seconds.', 'start': 85.541, 'duration': 4.021}, {'end': 96.605, 'text': "And now to generate a token, we can use the dump s method and I'm going to send in a payload that will just be our user ID.", 'start': 89.903, 'duration': 6.702}], 'summary': 'Using itsdangerous package to generate a secure time-sensitive token with a 30-second expiration time for user password reset.', 'duration': 76.601, 'max_score': 20.004, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI20004.jpg'}, {'end': 197.718, 'src': 'embed', 'start': 172.47, 'weight': 2, 'content': [{'end': 180.393, 'text': 'because it allows us to handle expirations and tokens without needing to put all of that in a database and make things more complicated than they need to be.', 'start': 172.47, 'duration': 7.923}, {'end': 188.435, 'text': "so now let's open up our database models and i'm going to add some methods to our user model that creates and validates these tokens for us.", 'start': 180.393, 'duration': 8.042}, {'end': 197.718, 'text': "so if we open up our project and navigate to our models.py file here and i already have this open then I'm going to go down to our user model.", 'start': 188.435, 'duration': 9.283}], 'summary': 'Implementing token management in user model for simplified database operations.', 'duration': 25.248, 'max_score': 172.47, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI172470.jpg'}], 'start': 0.229, 'title': 'Token generation and validation', 'summary': "Explains how to generate and validate a time-sensitive token using python and 'itsdangerous' package, with an expiration time of 30 seconds, and efficient handling of expirations without a database.", 'chapters': [{'end': 69.035, 'start': 0.229, 'title': 'Using email for password reset', 'summary': "Discusses how to generate a secure time-sensitive token to allow users to reset their passwords and how to send an email with reset password information using python and the package 'itsdangerous'. it also mentions the use of flask and demonstrates the usage of json web signature serializer for this process.", 'duration': 68.806, 'highlights': ["The chapter covers generating a secure time sensitive token to ensure only a specific user can reset their password using the package 'itsdangerous' and demonstrates the usage of JSON web signature serializer.", "It explains the process of sending an email with reset password information and mentions the importance of correctly spelling the camel casing for the import 'serializer'.", "The chapter uses Python and mentions the package 'itsdangerous' and Flask for generating a secure time-sensitive token and sending an email with reset password information."]}, {'end': 215.383, 'start': 69.555, 'title': 'Token generation and validation', 'summary': 'Explains the process of generating and validating a token with a serializer, including an expiration time of 30 seconds, and the ability to decode and check the validity of the token, enabling efficient handling of expirations without the need for a database.', 'duration': 145.828, 'highlights': ['The process of generating and validating a token with a serializer, including an expiration time of 30 seconds, and the ability to decode and check the validity of the token The chapter details the steps to generate and validate a token using a serializer with an expiration time of 30 seconds, providing a secure method for token management.', 'Efficient handling of token expirations without the need for a database The approach allows for efficient handling of token expirations without the necessity of using a database, simplifying the token management process and minimizing complexities.', 'The ability to decode and check the validity of the token The process includes the ability to decode and verify the validity of the token, ensuring secure and reliable token management.']}], 'duration': 215.154, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI229.jpg', 'highlights': ["The chapter covers generating a secure time sensitive token using 'itsdangerous' and demonstrates the usage of JSON web signature serializer.", 'The process of generating and validating a token with a serializer, including an expiration time of 30 seconds, and the ability to decode and check the validity of the token.', 'Efficient handling of token expirations without the need for a database, simplifying the token management process and minimizing complexities.', "The chapter uses Python and mentions the package 'itsdangerous' and Flask for generating a secure time-sensitive token and sending an email with reset password information."]}, {'end': 478.52, 'segs': [{'end': 294.891, 'src': 'embed', 'start': 235.335, 'weight': 0, 'content': [{'end': 242.5, 'text': "Okay, so now within our user model, now I'm going to create some methods here that make it easier to create these tokens.", 'start': 235.335, 'duration': 7.165}, {'end': 249.746, 'text': "So I'm going to create one method called get underscore reset underscore token.", 'start': 242.801, 'duration': 6.945}, {'end': 259.956, 'text': 'And we will take in self and expires underscore seconds for how many seconds until it expires.', 'start': 250.346, 'duration': 9.61}, {'end': 261.178, 'text': "And I'll pass in 1800 for now.", 'start': 260.277, 'duration': 0.901}, {'end': 262.159, 'text': 'And 1800 seconds is just 30 minutes.', 'start': 261.278, 'duration': 0.881}, {'end': 271.888, 'text': "So now let's go inside of our method here and we will create a serializer object.", 'start': 265.442, 'duration': 6.446}, {'end': 274.491, 'text': "So we'll say s equals serializer.", 'start': 272.209, 'duration': 2.282}, {'end': 277.133, 'text': 'And now we pass in our secret key.', 'start': 274.991, 'duration': 2.142}, {'end': 281.157, 'text': 'So in our example, in the command line, we just use the string secret.', 'start': 277.534, 'duration': 3.623}, {'end': 284.14, 'text': "But now we're actually going to use our app secret key.", 'start': 281.477, 'duration': 2.663}, {'end': 290.226, 'text': "So we'll say app.config and access that secret underscore key.", 'start': 284.24, 'duration': 5.986}, {'end': 294.891, 'text': 'And now we need our seconds and expiration time.', 'start': 290.927, 'duration': 3.964}], 'summary': "Creating a method to generate reset tokens with 1800 seconds expiration time using the app's secret key.", 'duration': 59.556, 'max_score': 235.335, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI235335.jpg'}, {'end': 398.632, 'src': 'embed', 'start': 345.801, 'weight': 3, 'content': [{'end': 349.522, 'text': "I won't touch on those object oriented specifics in this flask series.", 'start': 345.801, 'duration': 3.721}, {'end': 352.582, 'text': 'So we saw everything here before in our command line.', 'start': 349.902, 'duration': 2.68}, {'end': 358.764, 'text': "We're simply setting this up with our secret key and an expiration time, which by default is 30 minutes.", 'start': 353.062, 'duration': 5.702}, {'end': 363.908, 'text': "And then we're returning that token which is created by the dumps method.", 'start': 359.424, 'duration': 4.484}, {'end': 367.37, 'text': 'And it also contains a payload of the current user ID.', 'start': 364.328, 'duration': 3.042}, {'end': 371.334, 'text': "So now let's put a method in place that verifies a token.", 'start': 367.751, 'duration': 3.583}, {'end': 379.16, 'text': 'So I can come down here and say def verify underscore reset token.', 'start': 371.694, 'duration': 7.466}, {'end': 383.663, 'text': 'And then we will take in a token as an argument.', 'start': 379.82, 'duration': 3.843}, {'end': 389.046, 'text': "And now we'll create a serializer object again with this secret key.", 'start': 383.963, 'duration': 5.083}, {'end': 393.008, 'text': "But we don't need to pass in the expires seconds this time.", 'start': 389.386, 'duration': 3.622}, {'end': 396.591, 'text': 'Now we could get an exception when we try to load this token.', 'start': 393.389, 'duration': 3.202}, {'end': 398.632, 'text': 'The token could be invalid.', 'start': 397.111, 'duration': 1.521}], 'summary': 'Setting up token with default expiration time of 30 minutes, containing user id payload and method to verify token.', 'duration': 52.831, 'max_score': 345.801, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI345801.jpg'}, {'end': 478.52, 'src': 'embed', 'start': 455.302, 'weight': 5, 'content': [{'end': 462.648, 'text': 'that takes a token as an argument and just creates a serializer and then tries to load that token.', 'start': 455.302, 'duration': 7.346}, {'end': 464.85, 'text': 'if we get an exception, it returns none.', 'start': 462.648, 'duration': 2.202}, {'end': 469.373, 'text': "if we don't get an exception, then it returns the user with that user ID.", 'start': 464.85, 'duration': 4.523}, {'end': 470.814, 'text': 'now one more thing here is.', 'start': 469.373, 'duration': 1.441}, {'end': 475.558, 'text': "we can see that this method doesn't do anything with the instance of this user.", 'start': 470.814, 'duration': 4.744}, {'end': 478.52, 'text': 'so, for example, it never uses that self variable.', 'start': 475.558, 'duration': 2.962}], 'summary': 'Method creates serializer, loads token, returns user or none based on exception', 'duration': 23.218, 'max_score': 455.302, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI455302.jpg'}], 'start': 215.683, 'title': 'Token creation and verification in flask', 'summary': 'Demonstrates creating a method to generate a reset token for user authentication, utilizing a serializer object to encrypt user id and expiration time, with the default expiration time set to 30 minutes. it also discusses the process of setting up and verifying a token in a flask application, including the default expiration time of 30 minutes and the method for verifying a token, handling exceptions, and returning the user id.', 'chapters': [{'end': 345.181, 'start': 215.683, 'title': 'Implementing token creation in flask', 'summary': 'Demonstrates creating a method to generate a reset token for user authentication, utilizing a serializer object to encrypt user id and expiration time, with the default expiration time set to 30 minutes.', 'duration': 129.498, 'highlights': ['Creating a method to generate a reset token The chapter focuses on creating a method called get_reset_token within the user model to facilitate the creation of tokens for user authentication.', 'Utilizing a serializer object to encrypt user ID and expiration time The method demonstrates the usage of a serializer object to encrypt the user ID and expiration time, ensuring secure token creation for user authentication.', 'Setting default expiration time to 30 minutes The chapter sets the default expiration time for the tokens to 30 minutes, enhancing the security of the user authentication process.']}, {'end': 478.52, 'start': 345.801, 'title': 'Flask token verification', 'summary': 'Discusses the process of setting up and verifying a token in a flask application, including the default expiration time of 30 minutes and the method for verifying a token, handling exceptions, and returning the user id.', 'duration': 132.719, 'highlights': ['The chapter explains the process of setting up a token in a Flask application, which includes using a secret key and a default expiration time of 30 minutes.', "It details the method for verifying a token, handling potential exceptions, and returning the user ID from the token's payload.", "The method for verifying the token does not utilize the instance of the user, as it does not use the 'self' variable."]}], 'duration': 262.837, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI215683.jpg', 'highlights': ['Creating a method to generate a reset token within the user model', 'Utilizing a serializer object to encrypt user ID and expiration time', 'Setting default expiration time to 30 minutes for tokens', 'Explaining the process of setting up a token in a Flask application', 'Detailing the method for verifying a token and handling exceptions', 'Not utilizing the instance of the user in the token verification method']}, {'end': 757.795, 'segs': [{'end': 532.883, 'src': 'embed', 'start': 502.16, 'weight': 0, 'content': [{'end': 504.923, 'text': "And we're only going to be accepting this token as an argument.", 'start': 502.16, 'duration': 2.763}, {'end': 507.985, 'text': 'Okay, so now we have a way to generate and validate tokens.', 'start': 505.203, 'duration': 2.782}, {'end': 517.312, 'text': "So now let's actually create the routes for a user to reset their password and also see how to send an email with a link that they can use to do that.", 'start': 508.465, 'duration': 8.847}, {'end': 524.077, 'text': "So first of all, we're going to need two new forms for our routes and these are going to be extremely simple forms.", 'start': 517.672, 'duration': 6.405}, {'end': 532.883, 'text': "So let's open up our forms.py file and I'll come down here to the bottom of this file to create a new form.", 'start': 524.377, 'duration': 8.506}], 'summary': 'Creating routes for user password reset and email link with token validation.', 'duration': 30.723, 'max_score': 502.16, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI502160.jpg'}, {'end': 603.387, 'src': 'embed', 'start': 578.435, 'weight': 2, 'content': [{'end': 584.68, 'text': 'But the label that we want for this is going to be request password reset.', 'start': 578.435, 'duration': 6.245}, {'end': 588.301, 'text': "And let's also validate that an account exists for this email address.", 'start': 585.12, 'duration': 3.181}, {'end': 593.423, 'text': "Now, if we don't have an account for this address, then they haven't forgot their password.", 'start': 588.661, 'duration': 4.762}, {'end': 595.144, 'text': "They just haven't created an account.", 'start': 593.463, 'duration': 1.681}, {'end': 599.886, 'text': 'And this is going to be similar to the validation that we do upon registration.', 'start': 595.604, 'duration': 4.282}, {'end': 603.387, 'text': "So let's just grab that from the registration form.", 'start': 600.206, 'duration': 3.181}], 'summary': "Developing a 'request password reset' feature, including account validation for email address.", 'duration': 24.952, 'max_score': 578.435, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI578435.jpg'}, {'end': 657.376, 'src': 'embed', 'start': 630.021, 'weight': 1, 'content': [{'end': 632.883, 'text': "And now let's also change the validation message as well.", 'start': 630.021, 'duration': 2.862}, {'end': 642.71, 'text': 'So instead of saying that this email is taken, we can say there is no account with that email, you must register first.', 'start': 633.224, 'duration': 9.486}, {'end': 649.832, 'text': "Okay, and we're also going to want a form for when they actually do reset their password.", 'start': 645.31, 'duration': 4.522}, {'end': 657.376, 'text': 'And that will be a form with two password fields where they can type their new password and then confirm that password.', 'start': 650.272, 'duration': 7.104}], 'summary': 'Changing validation message, adding password reset form.', 'duration': 27.355, 'max_score': 630.021, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI630021.jpg'}, {'end': 736.026, 'src': 'embed', 'start': 685.363, 'weight': 5, 'content': [{'end': 690.164, 'text': "and then this will inherit from flask form, like we've seen before.", 'start': 685.363, 'duration': 4.801}, {'end': 698.125, 'text': "and now I'm going to paste in those two fields, the password and the confirm password field, and we're also going to want a submit button.", 'start': 690.164, 'duration': 7.961}, {'end': 704.707, 'text': 'so I will grab that from one of our other forms here and paste this in.', 'start': 698.125, 'duration': 6.582}, {'end': 711.528, 'text': 'but we want to change the label here for this submit button and we will just change this to reset password.', 'start': 704.707, 'duration': 6.821}, {'end': 715.891, 'text': 'Okay so now we need to create the routes and the templates that will handle these.', 'start': 712.168, 'duration': 3.723}, {'end': 717.972, 'text': "So first let's do the route.", 'start': 716.231, 'duration': 1.741}, {'end': 721.755, 'text': "So I'm going to open up routes.py file here and I'll come down.", 'start': 718.093, 'duration': 3.662}, {'end': 725.718, 'text': 'Actually, first we need to import those new forms that we just created.', 'start': 722.196, 'duration': 3.522}, {'end': 736.026, 'text': 'So right after our post form, I will import the request reset form and I will also import the reset password form.', 'start': 725.958, 'duration': 10.068}], 'summary': 'Creating routes and templates for handling new forms and importing request reset form and reset password form.', 'duration': 50.663, 'max_score': 685.363, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI685363.jpg'}], 'start': 478.94, 'title': 'User password reset process', 'summary': 'Covers creating a static method to generate and validate tokens, simple forms for user password reset, requesting a password reset, email validation, account existence check, reset password form creation, and route handling.', 'chapters': [{'end': 578.035, 'start': 478.94, 'title': 'Creating user password reset form', 'summary': 'Covers creating a static method to generate and validate tokens, followed by the creation of simple forms for user password reset, including an email submission form.', 'duration': 99.095, 'highlights': ['Creating a static method to generate and validate tokens The chapter explains how to create a static method in Python to generate and validate tokens for user authentication.', 'Creating simple forms for user password reset The chapter guides the creation of simple forms for user password reset, including an email submission form and a form for submitting the email for account instructions.']}, {'end': 629.581, 'start': 578.435, 'title': 'Request password reset process', 'summary': 'Discusses the process of requesting a password reset, including the validation of the email address and the check for the existence of an account, aiming to ensure a smooth user experience.', 'duration': 51.146, 'highlights': ["The process involves labeling the action as 'request password reset' and validating the existence of an account for the provided email address.", 'The validation process is similar to the one used during registration, involving checking if the email does not exist in the system.', 'The chapter emphasizes the need for distinguishing between forgotten passwords and non-existent accounts for a seamless user experience.']}, {'end': 757.795, 'start': 630.021, 'title': 'Reset password form and routes', 'summary': 'Discusses creating a reset password form with two password fields for users to enter their new password and confirm it, and also explains the process of importing the newly created forms to handle routes.', 'duration': 127.774, 'highlights': ['Creating a reset password form with two password fields for new password and confirmation.', 'Importing the request reset form and the reset password form to handle routes.', "Changing the validation message for email availability from 'email is taken' to 'no account with that email, register first'.", "Adding a submit button with the label 'reset password' to the reset password form."]}], 'duration': 278.855, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI478940.jpg', 'highlights': ['Creating a static method to generate and validate tokens for user authentication', 'Creating simple forms for user password reset, including email submission and account instructions', "Labeling the action as 'request password reset' and validating account existence for the provided email", 'Emphasizing the need for distinguishing forgotten passwords and non-existent accounts', 'Creating a reset password form with two password fields for new password and confirmation', 'Importing the request reset form and the reset password form to handle routes', 'Changing the validation message for email availability', "Adding a submit button with the label 'reset password' to the reset password form"]}, {'end': 1182.212, 'segs': [{'end': 812.991, 'src': 'embed', 'start': 785.075, 'weight': 3, 'content': [{'end': 791.258, 'text': 'Now the URL for this route, we will set that equal to reset underscore password.', 'start': 785.075, 'duration': 6.183}, {'end': 794.06, 'text': 'For methods, we want both get and post.', 'start': 791.719, 'duration': 2.341}, {'end': 794.82, 'text': "So that's good.", 'start': 794.22, 'duration': 0.6}, {'end': 798.562, 'text': 'Now for the function name instead of reset password.', 'start': 795.2, 'duration': 3.362}, {'end': 805.326, 'text': "let's actually call this reset request, because we're going to have another route, that is, reset password as well.", 'start': 798.562, 'duration': 6.764}, {'end': 808.508, 'text': "where they're actually going to change their password,", 'start': 805.706, 'duration': 2.802}, {'end': 812.991, 'text': "but this is going to be the route where they're just putting in their email for us to send that information,", 'start': 808.508, 'duration': 4.483}], 'summary': 'Set route url to reset_password, include get and post methods, rename function to reset_request.', 'duration': 27.916, 'max_score': 785.075, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI785075.jpg'}, {'end': 861.094, 'src': 'embed', 'start': 829.661, 'weight': 0, 'content': [{'end': 831.382, 'text': 'Then that was this line here.', 'start': 829.661, 'duration': 1.721}, {'end': 837.985, 'text': 'So if current user is authenticated, then just return a redirect to the home URL.', 'start': 831.482, 'duration': 6.503}, {'end': 845.268, 'text': "So we can paste that in to our new route to make sure that they're logged out before they reset their password.", 'start': 838.385, 'duration': 6.883}, {'end': 847.149, 'text': "And now let's create our form.", 'start': 845.588, 'duration': 1.561}, {'end': 853.272, 'text': 'So we will say form is equal to and that is the request reset form.', 'start': 847.509, 'duration': 5.763}, {'end': 855.653, 'text': 'and now we can render a template.', 'start': 853.852, 'duration': 1.801}, {'end': 861.094, 'text': 'so I will grab this line up here just as a starting point.', 'start': 855.653, 'duration': 5.441}], 'summary': 'Implementing user authentication and password reset functionality in a web application.', 'duration': 31.433, 'max_score': 829.661, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI829661.jpg'}, {'end': 962.912, 'src': 'embed', 'start': 922.453, 'weight': 2, 'content': [{'end': 929.24, 'text': "So I'm going to copy all of the code from the login template and paste it into our reset request dot HTML template.", 'start': 922.453, 'duration': 6.787}, {'end': 936.445, 'text': "so now we have the code from our login template and now let's change this to be what we need for our request reset form.", 'start': 929.68, 'duration': 6.765}, {'end': 940.167, 'text': 'so first i will scroll up and change the legend.', 'start': 936.445, 'duration': 3.722}, {'end': 942.769, 'text': 'so here we have the legend on the line seven.', 'start': 940.167, 'duration': 2.602}, {'end': 950.997, 'text': 'so instead of login, i want this to be reset password, And within this form we only have an email and a submit field.', 'start': 942.769, 'duration': 8.228}, {'end': 955.663, 'text': 'So the first field that we have from our login form is already set to email.', 'start': 951.357, 'duration': 4.306}, {'end': 959.848, 'text': "So let's just keep that and everything else we can get rid of.", 'start': 956.023, 'duration': 3.825}, {'end': 962.912, 'text': "So I'll get rid of the form group for the password.", 'start': 959.949, 'duration': 2.963}], 'summary': 'Adapting the login template code for reset request form, updating legend and form fields.', 'duration': 40.459, 'max_score': 922.453, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI922453.jpg'}, {'end': 1017.695, 'src': 'embed', 'start': 986.228, 'weight': 6, 'content': [{'end': 989.032, 'text': 'so that should be good for our request reset form.', 'start': 986.228, 'duration': 2.804}, {'end': 992.756, 'text': 'And again, if you are following along and think that you may have missed something,', 'start': 989.532, 'duration': 3.224}, {'end': 997.261, 'text': 'then I do have the final versions of all these files for each video that you can download.', 'start': 992.756, 'duration': 4.505}, {'end': 999.944, 'text': 'And the link to that is in the description section below.', 'start': 997.641, 'duration': 2.303}, {'end': 1004.629, 'text': 'Okay, so that is good for creating the route where the user will request to reset their password.', 'start': 1000.264, 'duration': 4.365}, {'end': 1008.891, 'text': "now let's create the route where the user will actually reset their password.", 'start': 1005.029, 'duration': 3.862}, {'end': 1017.695, 'text': "so i will go back to our routes here and now we'll create a route where the user actually resets their password now to make sure it's actually them.", 'start': 1008.891, 'duration': 8.804}], 'summary': 'Creating routes for user password reset, final file versions available for download.', 'duration': 31.467, 'max_score': 986.228, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI986228.jpg'}, {'end': 1113.439, 'src': 'embed', 'start': 1089.694, 'weight': 1, 'content': [{'end': 1097.659, 'text': "So now at this point, when they go to this URL, we'll try to verify the token from the URL using the user method that we created just a minute ago.", 'start': 1089.694, 'duration': 7.965}, {'end': 1113.439, 'text': 'So down here we can say the user is equal to user dot verify underscore reset token and we can pass in that token from the URL.', 'start': 1098.099, 'duration': 15.34}], 'summary': 'Verify token from url using user method.', 'duration': 23.745, 'max_score': 1089.694, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1089694.jpg'}, {'end': 1194.879, 'src': 'embed', 'start': 1157.635, 'weight': 5, 'content': [{'end': 1162.777, 'text': 'And just so they know it can be expired as well, we can say invalid or expired token.', 'start': 1157.635, 'duration': 5.142}, {'end': 1172.805, 'text': "and then we can pass in a class here or a category that we're going to use as a class and we'll pass that in as a bootstrap warning class,", 'start': 1163.457, 'duration': 9.348}, {'end': 1182.212, 'text': 'which is a yellow outlined alert, and if they have an invalid or expired token, then we will just return them back to that reset request page.', 'start': 1172.805, 'duration': 9.407}, {'end': 1194.879, 'text': "so we'll do a return for a redirect and that redirect will be the URL for oops and that URL for will be for the reset request.", 'start': 1182.212, 'duration': 12.667}], 'summary': 'Handling expired or invalid tokens by redirecting to reset request page', 'duration': 37.244, 'max_score': 1157.635, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1157635.jpg'}], 'start': 758.917, 'title': 'Creating password reset route', 'summary': "Covers the creation of a new route for resetting passwords, setting the url as 'reset_password', requiring the user to be logged out, creating a form, rendering a template, and modifying the template to fit the reset password form. it also discusses creating routes for user password reset, including verifying the token from the url and handling invalid or expired tokens, with a reminder on where to download the final versions of the files for each video.", 'chapters': [{'end': 986.228, 'start': 758.917, 'title': 'Creating reset password route', 'summary': "Details the creation of a new route for resetting passwords, setting the url as 'reset_password', requiring the user to be logged out, creating a form, rendering a template, and modifying the template to fit the reset password form.", 'duration': 227.311, 'highlights': ["The chapter details the creation of a new route for resetting passwords, setting the URL as 'reset_password', requiring the user to be logged out, creating a form, rendering a template, and modifying the template to fit the reset password form.", "The URL for the route is set as 'reset_password' with both 'get' and 'post' methods allowed.", "A form named 'request_reset_form' is created for users to input their email for password reset.", "The template 'reset_request.html' is created and modified to fit the requirements of the reset password form, including changing the legend and removing unnecessary form elements."]}, {'end': 1182.212, 'start': 986.228, 'title': 'Creating route for password reset', 'summary': 'Discusses creating routes for user password reset, including verifying the token from the url and handling invalid or expired tokens, with a reminder on where to download the final versions of the files for each video.', 'duration': 195.984, 'highlights': ['The chapter emphasizes the creation of routes for user password reset, including verifying the token from the URL and handling invalid or expired tokens (line 35-44).', 'It is mentioned that the final versions of all files for each video can be downloaded, with the link provided in the description section below (line 2-5).', "The process of verifying the token from the URL using the user method is explained, including the functionality of the 'verify_reset_token' method and its validation process (line 47-58).", 'The conditional handling of invalid or expired tokens is described, with a message to the user and the use of a bootstrap warning class (line 61-72).']}], 'duration': 423.295, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI758917.jpg', 'highlights': ["The chapter details the creation of a new route for resetting passwords, setting the URL as 'reset_password', requiring the user to be logged out, creating a form, rendering a template, and modifying the template to fit the reset password form.", "The process of verifying the token from the URL using the user method is explained, including the functionality of the 'verify_reset_token' method and its validation process.", "A form named 'request_reset_form' is created for users to input their email for password reset.", "The URL for the route is set as 'reset_password' with both 'get' and 'post' methods allowed.", "The template 'reset_request.html' is created and modified to fit the requirements of the reset password form, including changing the legend and removing unnecessary form elements.", 'The conditional handling of invalid or expired tokens is described, with a message to the user and the use of a bootstrap warning class.', 'It is mentioned that the final versions of all files for each video can be downloaded, with the link provided in the description section below.']}, {'end': 1978.964, 'segs': [{'end': 1234.331, 'src': 'embed', 'start': 1207.304, 'weight': 0, 'content': [{'end': 1219.008, 'text': "So we will come down here and say the form is equal to reset password form and then we'll render a template for them to actually reset their password.", 'start': 1207.304, 'duration': 11.704}, {'end': 1230.19, 'text': "so I'll say return, render template and I'll just copy the code from this rendered template here, since it's so similar, and paste this in.", 'start': 1219.008, 'duration': 11.182}, {'end': 1234.331, 'text': 'but instead of this being the reset request dot HTML,', 'start': 1230.19, 'duration': 4.141}], 'summary': "Code will render a 'reset password' form for users.", 'duration': 27.027, 'max_score': 1207.304, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1207304.jpg'}, {'end': 1361.785, 'src': 'embed', 'start': 1328.958, 'weight': 2, 'content': [{'end': 1333.059, 'text': 'And these forms will submit back to the same route that they were rendered from.', 'start': 1328.958, 'duration': 4.101}, {'end': 1335.119, 'text': "And we're used to seeing that by now.", 'start': 1333.479, 'duration': 1.64}, {'end': 1339.22, 'text': "So let's go add a validate on submit conditional to our routes.", 'start': 1335.499, 'duration': 3.721}, {'end': 1341.881, 'text': 'to handle these forms being submitted.', 'start': 1339.6, 'duration': 2.281}, {'end': 1348.522, 'text': "so within our reset request route, right after we create our form, we'll handle this.", 'start': 1341.881, 'duration': 6.641}, {'end': 1353.743, 'text': 'if the form was submitted and validated, so we can do our normal conditional of.', 'start': 1348.522, 'duration': 5.221}, {'end': 1361.785, 'text': 'if form dot validate on submit, then at this point they have submitted an email into our form.', 'start': 1353.743, 'duration': 8.042}], 'summary': 'Adding a validate on submit conditional to route for handling form submissions.', 'duration': 32.827, 'max_score': 1328.958, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1328958.jpg'}, {'end': 1412.087, 'src': 'embed', 'start': 1385.842, 'weight': 5, 'content': [{'end': 1394.069, 'text': "okay, so after we have that user, we're going to want to send this user an email with their token that they can use to reset their password.", 'start': 1385.842, 'duration': 8.227}, {'end': 1397.252, 'text': 'So now we need to learn how to send emails.', 'start': 1394.47, 'duration': 2.782}, {'end': 1403.659, 'text': "So for now, I'm just going to create a function that will leave empty while we talk about what we need to send emails.", 'start': 1397.633, 'duration': 6.026}, {'end': 1412.087, 'text': "So I'm going to create a function right above here that is called send reset email.", 'start': 1404.019, 'duration': 8.068}], 'summary': 'Create a function to send reset email to users.', 'duration': 26.245, 'max_score': 1385.842, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1385842.jpg'}, {'end': 1496.248, 'src': 'heatmap', 'start': 1465.642, 'weight': 0.717, 'content': [{'end': 1472.909, 'text': "So I'll say return, redirect, and we want to redirect them to the URL for login.", 'start': 1465.642, 'duration': 7.267}, {'end': 1480.476, 'text': 'Okay, so now we need to actually fill out this logic and learn how to actually send this email.', 'start': 1473.67, 'duration': 6.806}, {'end': 1488.122, 'text': "Now to do this, we're going to use another Flask extension and this one is called Flask Mail and we can install this with pip.", 'start': 1480.836, 'duration': 7.286}, {'end': 1496.248, 'text': "So if we open our terminal here, I'm going to exit out of our Python interpreter and we can install this simply by saying pip,", 'start': 1488.462, 'duration': 7.786}], 'summary': 'Learning to use flask mail for sending email with pip', 'duration': 30.606, 'max_score': 1465.642, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1465642.jpg'}, {'end': 1543.782, 'src': 'embed', 'start': 1516.397, 'weight': 1, 'content': [{'end': 1521.52, 'text': "So we'll say from flask underscore mail import mail.", 'start': 1516.397, 'duration': 5.123}, {'end': 1524.343, 'text': 'And now we need to also set some constants here.', 'start': 1521.92, 'duration': 2.423}, {'end': 1528.747, 'text': 'So this is going to be how our app actually knows how to send mail.', 'start': 1524.723, 'duration': 4.024}, {'end': 1538.317, 'text': "So we're going to need a mail server, a mail port, whether to use TLS and also a username and password for that server.", 'start': 1529.128, 'duration': 9.189}, {'end': 1541.02, 'text': "Now I'm going to be using Gmail in this example.", 'start': 1538.617, 'duration': 2.403}, {'end': 1543.782, 'text': 'But if you have another one, then you can use that as well.', 'start': 1541.36, 'duration': 2.422}], 'summary': 'Setting up constants for sending mail via flask, including mail server, port, tls, username, and password.', 'duration': 27.385, 'max_score': 1516.397, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1516397.jpg'}, {'end': 1650.082, 'src': 'heatmap', 'start': 1618.398, 'weight': 0.704, 'content': [{'end': 1621.98, 'text': "So if you don't know how to do this, then I would give that video a watch.", 'start': 1618.398, 'duration': 3.582}, {'end': 1625.042, 'text': 'I show how to do it for both Mac, Linux, and Windows.', 'start': 1622.08, 'duration': 2.962}, {'end': 1630.347, 'text': 'So first, to use the environment variables, I need to import the OS module.', 'start': 1625.502, 'duration': 4.845}, {'end': 1633.971, 'text': "So at the top of our module here, I'm going to import OS.", 'start': 1630.648, 'duration': 3.323}, {'end': 1641.54, 'text': 'And now I can set these variables by changing this to mail username.', 'start': 1634.632, 'duration': 6.908}, {'end': 1645.841, 'text': 'And that username is going to be an environment variable.', 'start': 1642.22, 'duration': 3.621}, {'end': 1650.082, 'text': "So I'm going to say os.environ.get.", 'start': 1645.861, 'duration': 4.221}], 'summary': 'Video tutorial covers setting environment variables for mac, linux, and windows.', 'duration': 31.684, 'max_score': 1618.398, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1618398.jpg'}, {'end': 1772.886, 'src': 'heatmap', 'start': 1667.586, 'weight': 1, 'content': [{'end': 1672.869, 'text': 'And for my environment variable here, I only did an email underscore pass.', 'start': 1667.586, 'duration': 5.283}, {'end': 1678.932, 'text': 'Okay, and once we have these five configurations set, then we can initialize our extension.', 'start': 1673.209, 'duration': 5.723}, {'end': 1682.614, 'text': "And we can initialize our extension just like we've done the others.", 'start': 1679.393, 'duration': 3.221}, {'end': 1689.16, 'text': "So I'll say mail is equal to mail and we'll pass in the app to initialize that.", 'start': 1682.955, 'duration': 6.205}, {'end': 1694.326, 'text': "Now that we have all of that set, let's open back up our routes.py file.", 'start': 1689.22, 'duration': 5.106}, {'end': 1699.972, 'text': "Now we're going to need our mail instance that we just created in our init.py file.", 'start': 1694.566, 'duration': 5.406}, {'end': 1702.094, 'text': "Let's go import that.", 'start': 1700.012, 'duration': 2.082}, {'end': 1710.019, 'text': "Here at the top of our file, where we're importing everything else from our package, we can add that on to our import.", 'start': 1702.174, 'duration': 7.845}, {'end': 1712.74, 'text': 'So from flask blog, import mail.', 'start': 1710.159, 'duration': 2.581}, {'end': 1715.342, 'text': "And we're also going to want to send an email message.", 'start': 1713.061, 'duration': 2.281}, {'end': 1719.683, 'text': 'So we need to import the message class from flask mail as well.', 'start': 1715.662, 'duration': 4.021}, {'end': 1727.767, 'text': "So down here at the bottom, I'm going to say from flask underscore mail, import message.", 'start': 1720.064, 'duration': 7.703}, {'end': 1735.408, 'text': "Okay, so now let's go down to our send email function that we just created down here close to the bottom.", 'start': 1728.547, 'duration': 6.861}, {'end': 1736.949, 'text': 'This is getting a little large.', 'start': 1735.448, 'duration': 1.501}, {'end': 1742.489, 'text': "So now let's fill in this function to send the user an email with the reset token.", 'start': 1737.289, 'duration': 5.2}, {'end': 1748.07, 'text': "So first let's get that token using the method that we added earlier to our user model.", 'start': 1742.87, 'duration': 5.2}, {'end': 1756.532, 'text': 'So we can say token is equal to user dot get underscore reset token.', 'start': 1748.471, 'duration': 8.061}, {'end': 1769.022, 'text': 'And if we look at our models, we can see that that takes also an argument for the seconds to expire, but it has a default there as well.', 'start': 1757.513, 'duration': 11.509}, {'end': 1772.886, 'text': "So we're just going to leave that as the default and not pass anything else in.", 'start': 1769.062, 'duration': 3.824}], 'summary': 'Configuring email settings and initializing extensions for sending email messages in flask application.', 'duration': 105.3, 'max_score': 1667.586, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1667586.jpg'}, {'end': 1806.463, 'src': 'embed', 'start': 1773.326, 'weight': 4, 'content': [{'end': 1777.368, 'text': 'and now we want to send the email with the URL, with the reset token.', 'start': 1773.326, 'duration': 4.042}, {'end': 1782.531, 'text': 'so we can create our email using the message class that we just imported.', 'start': 1777.368, 'duration': 5.163}, {'end': 1791.917, 'text': 'so we can say msg for message is equal to message and first this is going to be the subject line.', 'start': 1782.531, 'duration': 9.386}, {'end': 1798.14, 'text': 'so we can say password reset request and now we can specify the sender.', 'start': 1791.917, 'duration': 6.223}, {'end': 1806.463, 'text': "Now I'm just going to set a sender equal to noreplyatdemo.com.", 'start': 1799.281, 'duration': 7.182}], 'summary': 'Creating an email with password reset url and sender as noreply@demo.com.', 'duration': 33.137, 'max_score': 1773.326, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1773326.jpg'}, {'end': 1978.964, 'src': 'heatmap', 'start': 1920.415, 'weight': 0.736, 'content': [{'end': 1928.797, 'text': "Now we need to add in one more argument here and this is going to be underscore external And we're going to set this equal to true.", 'start': 1920.415, 'duration': 8.382}, {'end': 1939.323, 'text': 'And now, just to continue our message here, I will write at the bottom of this email if you did not make this request,', 'start': 1929.598, 'duration': 9.725}, {'end': 1946.767, 'text': 'then simply ignore this email and no changes will be made.', 'start': 1939.323, 'duration': 7.444}, {'end': 1950.889, 'text': "And then I'll just bring this back here to the baseline there.", 'start': 1947.427, 'duration': 3.462}, {'end': 1956.347, 'text': "Now, when you're working with a multi-line string like this, then you do need to bring this text back to the baseline here.", 'start': 1951.263, 'duration': 5.084}, {'end': 1962.151, 'text': 'Because if you tab these over, then those tabs will actually show up in the string.', 'start': 1956.427, 'duration': 5.724}, {'end': 1965.314, 'text': 'So you want to make sure that these are back at the baseline.', 'start': 1962.171, 'duration': 3.143}, {'end': 1974.06, 'text': "Now, the reason that we're using this external equal to true here is in order to get an absolute URL rather than a relative URL.", 'start': 1965.574, 'duration': 8.486}, {'end': 1978.964, 'text': "Because relative URLs are fine within our application, and that's what it normally returns.", 'start': 1974.701, 'duration': 4.263}], 'summary': "Adding 'external' argument as 'true' to get absolute url rather than a relative url.", 'duration': 58.549, 'max_score': 1920.415, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1920415.jpg'}], 'start': 1182.212, 'title': 'Creating reset password form and flask email configuration', 'summary': 'Covers creating a reset password form, including rendering template, updating fields, and handling form submission, and setting up email configuration using flask-mail with details on constants for mail server, port, tls, username, password, sending reset email, and constructing an email message.', 'chapters': [{'end': 1353.743, 'start': 1182.212, 'title': 'Reset password form', 'summary': 'Outlines the process of creating a reset password form, including the steps for rendering the template, updating the fields, and handling form submission within the route.', 'duration': 171.531, 'highlights': ['The chapter illustrates the process of creating a reset password form by rendering the template, updating the fields, and handling form submission within the route.', 'It explains the steps to render the reset password form template and update the fields by replacing the email field with password and confirm password fields.', 'The chapter also covers the handling of form submission within the route by adding a validate on submit conditional to the routes.']}, {'end': 1978.964, 'start': 1353.743, 'title': 'Flask email configuration', 'summary': 'Covers setting up email configuration using flask-mail extension, including setting up constants for mail server, port, tls, username, and password, as well as creating a function to send reset email and constructing an email message with a reset token url.', 'duration': 625.221, 'highlights': ['Setting up email configuration using Flask-Mail extension The chapter covers setting up email configuration using Flask-Mail extension, including setting up constants for mail server, port, TLS, username, and password.', 'Creating a function to send reset email The author creates a function called send reset email that takes a user as an argument and leaves it empty for now, with the intention to fill out the logic later.', "Constructing an email message with a reset token URL The chapter explains how to construct an email message with a subject line 'password reset request', sender email address, recipient email, and a message body containing a URL for the reset token route."]}], 'duration': 796.752, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1182212.jpg', 'highlights': ['The chapter illustrates the process of creating a reset password form by rendering the template, updating the fields, and handling form submission within the route.', 'Setting up email configuration using Flask-Mail extension, including setting up constants for mail server, port, TLS, username, and password.', 'The chapter also covers the handling of form submission within the route by adding a validate on submit conditional to the routes.', 'The chapter covers setting up email configuration using Flask-Mail extension, including setting up constants for mail server, port, TLS, username, and password.', 'Constructing an email message with a reset token URL, including subject line, sender email address, recipient email, and a message body containing a URL for the reset token route.', 'Creating a function to send reset email, leaving it empty for now, with the intention to fill out the logic later.']}, {'end': 2518.961, 'segs': [{'end': 2079.455, 'src': 'heatmap', 'start': 2021.557, 'weight': 0.846, 'content': [{'end': 2026.901, 'text': "So I'm going to go to the register route and I'm going to grab a bit of code from there.", 'start': 2021.557, 'duration': 5.344}, {'end': 2033.065, 'text': 'So if I go up to the register route, where is it at? Here it is.', 'start': 2027.061, 'duration': 6.004}, {'end': 2040.471, 'text': "Then I'm just going to grab this entire if form validate on submit conditional all the way down to the return statement.", 'start': 2033.085, 'duration': 7.386}, {'end': 2044.073, 'text': "And now let's paste this below.", 'start': 2041.011, 'duration': 3.062}, {'end': 2051.697, 'text': "If I go down to our reset token route, let's paste this below where we are creating that form.", 'start': 2044.093, 'duration': 7.604}, {'end': 2056.1, 'text': 'So if I paste that in there, then there are only going to be a couple of differences here.', 'start': 2052.018, 'duration': 4.082}, {'end': 2061.063, 'text': 'So first of all, we already have the user from where we verified the token.', 'start': 2056.82, 'duration': 4.243}, {'end': 2066.346, 'text': "so we can remove this line where we're getting the user.", 'start': 2061.463, 'duration': 4.883}, {'end': 2069.668, 'text': "and again, that's because we have the user up here already.", 'start': 2066.346, 'duration': 3.322}, {'end': 2074.011, 'text': 'and also, instead of adding this user to the database, like we did in the register route,', 'start': 2069.668, 'duration': 4.343}, {'end': 2079.455, 'text': "we're instead just going to edit their password and set it to this new hash password.", 'start': 2074.011, 'duration': 5.444}], 'summary': 'Code snippets are being copied and pasted from the register route to the reset token route, with modifications including user verification and password editing.', 'duration': 57.898, 'max_score': 2021.557, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI2021557.jpg'}, {'end': 2144.373, 'src': 'embed', 'start': 2114.395, 'weight': 4, 'content': [{'end': 2122.647, 'text': "let's say your password has been updated and then leave the part where it says you are now able to log in.", 'start': 2114.395, 'duration': 8.252}, {'end': 2126.208, 'text': 'and then redirect them to the login page.', 'start': 2123.187, 'duration': 3.021}, {'end': 2127.368, 'text': "so that's good, okay.", 'start': 2126.208, 'duration': 1.16}, {'end': 2136.09, 'text': 'so last quick step before we run our application and test all this out, we need to actually add a link to the reset password page in our application.', 'start': 2127.368, 'duration': 8.722}, {'end': 2144.373, 'text': "now, if you remember back to one of our earlier videos, we put a forgot password link on the login page, but the link didn't actually go anywhere.", 'start': 2136.09, 'duration': 8.283}], 'summary': 'Update password, redirect to login page, add reset password link.', 'duration': 29.978, 'max_score': 2114.395, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI2114395.jpg'}, {'end': 2255.286, 'src': 'heatmap', 'start': 2223.533, 'weight': 0.871, 'content': [{'end': 2229.14, 'text': 'So if we go to our login page here, then we can see that we have our forgot password link here.', 'start': 2223.533, 'duration': 5.607}, {'end': 2232.484, 'text': 'Now that should be beside the login button.', 'start': 2230.021, 'duration': 2.463}, {'end': 2233.685, 'text': "It's not a big deal, but.", 'start': 2232.524, 'duration': 1.161}, {'end': 2246.417, 'text': 'If I open up the login template, this forgot password small tag here should actually be inside this form group right beside our submit button.', 'start': 2234.586, 'duration': 11.831}, {'end': 2249.981, 'text': 'And that will put that actually beside the button.', 'start': 2246.978, 'duration': 3.003}, {'end': 2255.286, 'text': "So if I save that and reload the page, then we can see that now that's beside the login button.", 'start': 2250.281, 'duration': 5.005}], 'summary': 'The forgot password link should be positioned beside the login button, improving user experience.', 'duration': 31.753, 'max_score': 2223.533, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI2223533.jpg'}, {'end': 2282.948, 'src': 'embed', 'start': 2255.666, 'weight': 2, 'content': [{'end': 2266.795, 'text': 'So if I click on the forgot password link, then it takes us to our reset password route and we can put in our email and request a password reset.', 'start': 2255.666, 'duration': 11.129}, {'end': 2272.779, 'text': "So I'm going to request a password reset for Coreymschafer at gmail.com.", 'start': 2267.095, 'duration': 5.684}, {'end': 2275.982, 'text': 'So I will request that password reset.', 'start': 2273.86, 'duration': 2.122}, {'end': 2282.948, 'text': 'We can see that we got a notice alert here that says an email has been sent with instructions to reset your password.', 'start': 2276.442, 'duration': 6.506}], 'summary': 'User requested a password reset for coreymschafer at gmail.com, and a notice alert confirmed that an email was sent with reset instructions.', 'duration': 27.282, 'max_score': 2255.666, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI2255666.jpg'}, {'end': 2415.687, 'src': 'embed', 'start': 2389.709, 'weight': 3, 'content': [{'end': 2395.658, 'text': "But if this token wasn't valid or if this token was expired because it was older than 30 minutes,", 'start': 2389.709, 'duration': 5.949}, {'end': 2398.102, 'text': 'which was the expiration time that we put on this token.', 'start': 2395.658, 'duration': 2.444}, {'end': 2403.964, 'text': 'then it would redirect us back to I believe the reset password route.', 'start': 2398.762, 'duration': 5.202}, {'end': 2407.565, 'text': 'So if I was to change one character up here,', 'start': 2404.244, 'duration': 3.321}, {'end': 2415.687, 'text': 'then we can see that it redirected us back to this request password reset form and says that that is an invalid or expired token.', 'start': 2407.565, 'duration': 8.122}], 'summary': 'Expired tokens redirect to password reset route after 30 minutes.', 'duration': 25.978, 'max_score': 2389.709, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI2389709.jpg'}, {'end': 2495.204, 'src': 'embed', 'start': 2466.841, 'weight': 0, 'content': [{'end': 2469.323, 'text': 'but other than that, the functionality worked well.', 'start': 2466.841, 'duration': 2.482}, {'end': 2474.327, 'text': "we got our email and we're able to reset our password with our valid token.", 'start': 2469.323, 'duration': 5.004}, {'end': 2476.589, 'text': 'okay. so i think that is going to do it for this video.', 'start': 2474.327, 'duration': 2.262}, {'end': 2483.955, 'text': 'i hope that now you have a good idea for how you can handle resetting passwords and also how to send mail through a flask application,', 'start': 2476.589, 'duration': 7.366}, {'end': 2495.204, 'text': 'and in the next video we will clean up our application a little bit by learning how to organize different parts into flask blueprints and also how to move our configuration variables into one single place.', 'start': 2483.955, 'duration': 11.249}], 'summary': 'Functionality worked well, able to reset password and send mail through flask.', 'duration': 28.363, 'max_score': 2466.841, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI2466841.jpg'}], 'start': 1979.304, 'title': 'Setting up password reset in python and reset password process in flask application', 'summary': 'Covers the process of setting up password reset functionality in a python application with a focus on testing functionality and ensuring proper display. it also demonstrates the process of resetting passwords in a flask application, ensuring functionalities such as email sending and password resetting work seamlessly.', 'chapters': [{'end': 2255.286, 'start': 1979.304, 'title': 'Setting up password reset in python', 'summary': 'Covers the process of setting up password reset functionality in a python application, including using jinja2 templates to construct email messages, handling form submissions for password changes, and updating flash messages and urls in the application, with a focus on testing functionality and ensuring proper display.', 'duration': 275.982, 'highlights': ['The process of setting up password reset functionality in a Python application is covered, including using Jinja2 templates to construct email messages, handling form submissions for password changes, and updating flash messages and URLs in the application.', 'Demonstrates the process of handling form submissions for password changes, including grabbing code from the register route, making necessary modifications, and updating flash messages.', 'Discusses the placement of the forgot password link on the login page, the process of updating URLs, and the importance of testing functionality and ensuring proper display.']}, {'end': 2518.961, 'start': 2255.666, 'title': 'Reset password process in flask application', 'summary': 'Demonstrates the process of resetting passwords in a flask application, covering the steps of requesting a password reset, sending a reset email, validating the token, and updating the password, ensuring functionalities such as email sending and password resetting work seamlessly.', 'duration': 263.295, 'highlights': ['The process of resetting passwords in a Flask application is demonstrated, covering the steps of requesting a password reset, sending a reset email, validating the token, and updating the password. The chapter provides a detailed walkthrough of the entire process of resetting passwords in a Flask application, including requesting a password reset, sending a reset email, validating the token, and updating the password.', 'An email has been sent with instructions to reset the password. Upon requesting a password reset, an email is sent with instructions to reset the password, confirming the successful initiation of the reset process.', 'A token expiration time of 30 minutes is set for the password reset process. A token expiration time of 30 minutes is set for the password reset process, ensuring that the token remains valid for a specific duration.', 'Upon successful password reset, an alert confirms the update of the password. After successfully resetting the password, an alert confirms the update of the password, providing immediate feedback to the user.']}], 'duration': 539.657, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/vutyTx7IaAI/pics/vutyTx7IaAI1979304.jpg', 'highlights': ['Covers the process of setting up password reset functionality in a Python application, including using Jinja2 templates, handling form submissions, and updating flash messages and URLs.', 'Demonstrates the process of resetting passwords in a Flask application, covering steps such as requesting a password reset, sending a reset email, validating the token, and updating the password.', 'An email is sent with instructions to reset the password upon requesting a password reset, confirming the successful initiation of the reset process.', 'A token expiration time of 30 minutes is set for the password reset process, ensuring that the token remains valid for a specific duration.', 'Upon successful password reset, an alert confirms the update of the password, providing immediate feedback to the user.', 'Discusses the placement of the forgot password link on the login page, the process of updating URLs, and the importance of testing functionality and ensuring proper display.']}], 'highlights': ["The chapter covers generating a secure time sensitive token using 'itsdangerous' and demonstrates the usage of JSON web signature serializer.", 'Creating a method to generate a reset token within the user model', 'Creating a static method to generate and validate tokens for user authentication', "The chapter details the creation of a new route for resetting passwords, setting the URL as 'reset_password', requiring the user to be logged out, creating a form, rendering a template, and modifying the template to fit the reset password form.", 'Setting up email configuration using Flask-Mail extension, including setting up constants for mail server, port, TLS, username, and password.', 'Covers the process of setting up password reset functionality in a Python application, including using Jinja2 templates, handling form submissions, and updating flash messages and URLs.']}