UPDATE: Almost fully implemented forget email feature

This commit is contained in:
JustIceO7
2025-02-06 19:30:37 +00:00
parent 00d627a1e2
commit bb67f13417
7 changed files with 63 additions and 52 deletions

View File

@@ -6,7 +6,10 @@ from random import randrange
from dotenv import load_dotenv
from utils.user_utils import generate_token
from secrets import token_hex
import redis
redis_url = "redis://redis:6379/1"
r = redis.from_url(redis_url, decode_responses=True)
load_dotenv()
@@ -24,7 +27,7 @@ def send_email(email, func) -> None:
# Setup up the receiver details
login_code = randrange(100000, 1000000)
body = func()
print(body, flush=True)
msg = MIMEText(body, "html")
msg["Subject"] = "Reset Gander Login"
msg["From"] = SMTP_EMAIL
@@ -45,8 +48,14 @@ def send_email(email, func) -> None:
print("Error: ", e, flush=True)
def forgot_password_body(email):
token = generate_token(email, token_hex(32))
"""
Handles the creation of the email body for resetting password
"""
salt = token_hex(32)
token = generate_token(email, salt)
url = getenv("VITE_API_URL")
r.setex(token, 3600, salt)
full_url = url + "/reset_password/" + token
content = f"""

View File

@@ -2,6 +2,10 @@ from flask import Blueprint, jsonify, session, abort, abort
from utils.user_utils import *
from blueprints.utils import login_required
from blueprints.email import send_email, forgot_password_body
import redis
redis_url = "redis://redis:6379/1"
r = redis.from_url(redis_url, decode_responses=True)
user_bp = Blueprint("user", __name__)
@@ -93,23 +97,27 @@ def get_login_status():
@user_bp.route('/user/forgot_password/<string:email>', methods=['GET','POST'])
def user_forgot_password(email):
"""
Will send link to email to reset password by looking at the user_id within session to see whos password should be reset
Creates a super random number to be used a the link to reset password I guess a random number generator seeded with a secret
Initializes the function to handle password reset
"""
send_email(email, lambda: forgot_password_body(email))
return email
@user_bp.route('/user/reset_password/<string:token>/<string:new_password>')
@user_bp.route('/user/reset_password/<string:token>/<string:new_password>', methods=['POST'])
def user_reset_password(token, new_password):
"""
Given token and new password resets the users password
"""
email = verify_token(token)
salt_value = r.get(token)
if salt_value:
r.delete(token)
email = verify_token(token, salt_value)
if email:
response = reset_password(new_password, email)
if response:
return 200
else:
abort(500)
return abort(500)
return abort(404)

View File

@@ -1,7 +1,7 @@
from database.database import Database
from typing import Optional, List
from datetime import datetime
from itsdangerous import URLSafeTimedSerializer
from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
from os import getenv
from werkzeug.security import generate_password_hash, check_password_hash
from dotenv import load_dotenv
@@ -145,10 +145,20 @@ def generate_token(email, salt_value) -> str:
def verify_token(token: str, salt_value) -> Optional[str]:
"""
Given a token verifies token and decodes the token into an email
Given a token, verifies and decodes it into an email
"""
email = serializer.loads(token, salt=salt_value, max_age=3600)
return email if email else False
try:
email = serializer.loads(token, salt=salt_value, max_age=3600)
return email
except SignatureExpired:
# Token expired
print("Token has expired", flush=True)
return None
except BadSignature:
# Invalid token
print("Token is invalid", flush=True)
return None
def reset_password(new_password: str, email: str) -> bool:
"""