Add password reset mechanism (Issue #1)

- Added reset_token and reset_token_expiry fields to User model
- Implemented generate_reset_token(), verify_reset_token(), and clear_reset_token() methods
- Created password reset request form (/password-reset-request)
- Created password reset form (/password-reset/<token>)
- Added "Forgot password?" link to login page
- Reset tokens expire after 1 hour for security
- Created migration script to add new database columns
- Reset links are logged (would be emailed in production)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-11 18:46:18 -05:00
parent a1d8c9d373
commit 51911f2c48
6 changed files with 244 additions and 0 deletions

View File

@@ -41,6 +41,10 @@ class User(UserMixin, db.Model):
# User settings (JSON stored as text)
settings = db.Column(db.Text, default='{}')
# Password reset
reset_token = db.Column(db.String(100), nullable=True, unique=True, index=True)
reset_token_expiry = db.Column(db.DateTime, nullable=True)
def __init__(self, username, email, password=None, is_admin=False, auth0_id=None):
"""
Initialize a new user.
@@ -102,6 +106,32 @@ class User(UserMixin, db.Model):
self.last_login = datetime.utcnow()
db.session.commit()
def generate_reset_token(self):
"""Generate a password reset token that expires in 1 hour"""
import secrets
from datetime import timedelta
self.reset_token = secrets.token_urlsafe(32)
self.reset_token_expiry = datetime.utcnow() + timedelta(hours=1)
db.session.commit()
return self.reset_token
def verify_reset_token(self, token):
"""Verify if the provided reset token is valid and not expired"""
if not self.reset_token or not self.reset_token_expiry:
return False
if self.reset_token != token:
return False
if datetime.utcnow() > self.reset_token_expiry:
return False
return True
def clear_reset_token(self):
"""Clear the reset token after use"""
self.reset_token = None
self.reset_token_expiry = None
db.session.commit()
def get_id(self):
"""Required by Flask-Login"""
return self.id