or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdauthorization.mdcore-setup.mddatabase.mdindex.mdpassword-management.mdregistration.mdtwo-factor.mdunified-signin.mdutilities.mdwebauthn.md

password-management.mddocs/

0

# Password Management

1

2

Password hashing, validation, reset workflows, security utilities, and password policy enforcement for managing user credentials securely in Flask applications.

3

4

## Capabilities

5

6

### Password Hashing and Verification

7

8

Core functions for secure password storage and verification using modern hashing algorithms.

9

10

```python { .api }

11

def hash_password(password):

12

"""

13

Hash a password using the configured hashing algorithm.

14

15

Parameters:

16

- password: Plain text password to hash

17

18

Returns:

19

Hashed password string suitable for database storage

20

"""

21

22

def verify_password(password, password_hash):

23

"""

24

Verify a password against its stored hash.

25

26

Parameters:

27

- password: Plain text password to verify

28

- password_hash: Stored password hash

29

30

Returns:

31

True if password matches hash, False otherwise

32

"""

33

34

def verify_and_update_password(password, user):

35

"""

36

Verify password and update hash if algorithm upgrade is needed.

37

38

Parameters:

39

- password: Plain text password to verify

40

- user: User object with password hash

41

42

Returns:

43

True if password matches, False otherwise

44

"""

45

```

46

47

### Password Change Forms

48

49

Forms for users to change their existing passwords.

50

51

```python { .api }

52

class ChangePasswordForm(Form):

53

"""

54

Form for users to change their current password.

55

56

Fields:

57

- password: Current password field for verification

58

- new_password: New password field

59

- new_password_confirm: New password confirmation field

60

- submit: Submit button

61

"""

62

password: PasswordField

63

new_password: PasswordField

64

new_password_confirm: PasswordField

65

submit: SubmitField

66

```

67

68

### Password Reset Forms

69

70

Forms for password recovery workflow when users forget their passwords.

71

72

```python { .api }

73

class ForgotPasswordForm(Form):

74

"""

75

Form for requesting password reset instructions.

76

77

Fields:

78

- user: Email or username field for identity lookup

79

- submit: Submit button

80

"""

81

user: StringField

82

submit: SubmitField

83

84

class ResetPasswordForm(Form):

85

"""

86

Form for setting new password during reset process.

87

88

Fields:

89

- password: New password field

90

- password_confirm: New password confirmation field

91

- submit: Submit button

92

"""

93

password: PasswordField

94

password_confirm: PasswordField

95

submit: SubmitField

96

```

97

98

### Password Reset Functions

99

100

Functions for managing the password reset workflow and token validation.

101

102

```python { .api }

103

def send_reset_password_instructions(user):

104

"""

105

Send password reset instructions via email.

106

107

Parameters:

108

- user: User object to send reset instructions to

109

110

Returns:

111

True if email sent successfully, False otherwise

112

"""

113

114

def send_password_reset_notice(user):

115

"""

116

Send notification that password was reset (security notice).

117

118

Parameters:

119

- user: User object whose password was reset

120

121

Returns:

122

True if email sent successfully, False otherwise

123

"""

124

125

def generate_reset_password_token(user):

126

"""

127

Generate password reset token for user.

128

129

Parameters:

130

- user: User object to generate token for

131

132

Returns:

133

Reset token string

134

"""

135

136

def reset_password_token_status(token):

137

"""

138

Validate password reset token and return status information.

139

140

Parameters:

141

- token: Reset token to validate

142

143

Returns:

144

Tuple of (expired: bool, invalid: bool, user: User|None)

145

"""

146

147

def update_password(user, password):

148

"""

149

Update user's password with proper hashing and validation.

150

151

Parameters:

152

- user: User object to update password for

153

- password: New plain text password

154

155

Returns:

156

True if password updated successfully, False otherwise

157

"""

158

```

159

160

### Administrative Password Functions

161

162

Functions for administrators to manage user passwords.

163

164

```python { .api }

165

def admin_change_password(user, password, notify=True):

166

"""

167

Administrative function to change user's password.

168

169

Parameters:

170

- user: User object whose password to change

171

- password: New plain text password

172

- notify: Whether to send notification email (default: True)

173

174

Returns:

175

True if password changed successfully, False otherwise

176

"""

177

```

178

179

### Password Validation Functions

180

181

Functions for validating password strength and security requirements.

182

183

```python { .api }

184

def password_length_validator(password, min_length=8, max_length=128):

185

"""

186

Validate password length requirements.

187

188

Parameters:

189

- password: Password to validate

190

- min_length: Minimum password length (default: 8)

191

- max_length: Maximum password length (default: 128)

192

193

Returns:

194

True if valid, False otherwise

195

196

Raises:

197

ValidationError: If password length is invalid

198

"""

199

200

def password_complexity_validator(password):

201

"""

202

Validate password complexity requirements.

203

204

Parameters:

205

- password: Password to validate

206

207

Returns:

208

True if password meets complexity requirements

209

210

Raises:

211

ValidationError: If password doesn't meet complexity requirements

212

"""

213

214

def password_breached_validator(password):

215

"""

216

Check if password appears in known breach databases.

217

218

Parameters:

219

- password: Password to check

220

221

Returns:

222

True if password is safe to use

223

224

Raises:

225

ValidationError: If password is found in breach database

226

"""

227

228

def pwned(password):

229

"""

230

Check if password appears in HaveIBeenPwned database.

231

232

Parameters:

233

- password: Password to check

234

235

Returns:

236

Number of times password appears in breaches (0 if safe)

237

"""

238

```

239

240

### Password Utility Class

241

242

Utility class for advanced password management operations.

243

244

```python { .api }

245

class PasswordUtil:

246

"""

247

Utility class for password management operations.

248

"""

249

250

def __init__(self, app=None):

251

"""Initialize password utility with Flask app."""

252

253

def hash_password(self, password):

254

"""Hash password using configured algorithm."""

255

256

def verify_password(self, password, password_hash):

257

"""Verify password against hash."""

258

259

def generate_password(self, length=12, include_symbols=True):

260

"""Generate secure random password."""

261

262

def check_password_strength(self, password):

263

"""Analyze password strength and return score."""

264

```

265

266

## Usage Examples

267

268

### Basic Password Change

269

270

```python

271

from flask_security import current_user, hash_password

272

273

@app.route('/change-password', methods=['GET', 'POST'])

274

@login_required

275

def change_password():

276

form = ChangePasswordForm()

277

if form.validate_on_submit():

278

if verify_password(form.password.data, current_user.password):

279

current_user.password = hash_password(form.new_password.data)

280

db.session.commit()

281

flash('Password changed successfully')

282

return redirect('/profile')

283

else:

284

flash('Current password is incorrect')

285

286

return render_template('change_password.html', form=form)

287

```

288

289

### Password Reset Workflow

290

291

```python

292

from flask_security import send_reset_password_instructions, reset_password_token_status

293

294

@app.route('/forgot-password', methods=['GET', 'POST'])

295

def forgot_password():

296

form = ForgotPasswordForm()

297

if form.validate_on_submit():

298

user = lookup_identity(form.user.data)

299

if user:

300

send_reset_password_instructions(user)

301

# Always show success message for security

302

flash('If your email is registered, you will receive reset instructions')

303

return redirect('/login')

304

305

return render_template('forgot_password.html', form=form)

306

307

@app.route('/reset-password/<token>', methods=['GET', 'POST'])

308

def reset_password(token):

309

expired, invalid, user = reset_password_token_status(token)

310

311

if expired:

312

flash('The reset link has expired')

313

return redirect('/forgot-password')

314

315

if invalid or not user:

316

flash('Invalid reset link')

317

return redirect('/login')

318

319

form = ResetPasswordForm()

320

if form.validate_on_submit():

321

update_password(user, form.password.data)

322

db.session.commit()

323

flash('Password reset successfully')

324

return redirect('/login')

325

326

return render_template('reset_password.html', form=form, token=token)

327

```

328

329

### Administrative Password Management

330

331

```python

332

from flask_security import admin_change_password

333

334

@app.route('/admin/reset-user-password/<int:user_id>', methods=['POST'])

335

@roles_required('admin')

336

def admin_reset_password(user_id):

337

user = User.query.get_or_404(user_id)

338

new_password = request.form.get('new_password')

339

340

if admin_change_password(user, new_password, notify=True):

341

db.session.commit()

342

flash(f'Password reset for user {user.email}')

343

else:

344

flash('Failed to reset password')

345

346

return redirect(f'/admin/user/{user_id}')

347

```

348

349

### Password Strength Validation

350

351

```python

352

from flask_security import password_length_validator, password_complexity_validator

353

from wtforms.validators import ValidationError

354

355

class StrongPasswordForm(Form):

356

password = PasswordField('Password', validators=[

357

DataRequired(),

358

Length(min=12, message='Password must be at least 12 characters'),

359

password_complexity_validator,

360

password_breached_validator

361

])

362

363

def validate_password(self, field):

364

# Custom password validation

365

password = field.data

366

367

# Check for common patterns

368

if password.lower() in ['password', '123456', 'qwerty']:

369

raise ValidationError('Password is too common')

370

371

# Require mixed case, numbers, and symbols

372

has_upper = any(c.isupper() for c in password)

373

has_lower = any(c.islower() for c in password)

374

has_digit = any(c.isdigit() for c in password)

375

has_symbol = any(c in '!@#$%^&*()' for c in password)

376

377

if not all([has_upper, has_lower, has_digit, has_symbol]):

378

raise ValidationError(

379

'Password must contain uppercase, lowercase, numbers, and symbols'

380

)

381

```

382

383

### Password Breach Checking

384

385

```python

386

from flask_security import pwned

387

388

@app.route('/check-password-safety', methods=['POST'])

389

@login_required

390

def check_password_safety():

391

password = request.form.get('password')

392

393

try:

394

breach_count = pwned(password)

395

if breach_count > 0:

396

return jsonify({

397

'safe': False,

398

'message': f'Password found in {breach_count} data breaches',

399

'recommendation': 'Choose a different password'

400

})

401

else:

402

return jsonify({

403

'safe': True,

404

'message': 'Password not found in known breaches'

405

})

406

except Exception as e:

407

return jsonify({

408

'safe': None,

409

'message': 'Unable to check password safety',

410

'error': str(e)

411

})

412

```

413

414

### Custom Password Policy

415

416

```python

417

from flask_security import Security

418

from passlib.context import CryptContext

419

420

# Custom password context with specific algorithms

421

pwd_context = CryptContext(

422

schemes=['bcrypt', 'argon2'],

423

default='argon2',

424

bcrypt__min_rounds=12,

425

argon2__time_cost=16,

426

argon2__memory_cost=102400,

427

argon2__parallelism=8

428

)

429

430

app.config['SECURITY_PASSWORD_HASH'] = 'argon2'

431

app.config['SECURITY_PASSWORD_LENGTH_MIN'] = 12

432

app.config['SECURITY_PASSWORD_COMPLEXITY_CHECKER'] = 'zxcvbn'

433

app.config['SECURITY_PASSWORD_CHECK_BREACHED'] = True

434

app.config['SECURITY_PASSWORD_BREACHED_COUNT'] = 1

435

436

security = Security(app, user_datastore)

437

```

438

439

### Password History Prevention

440

441

```python

442

class UserWithPasswordHistory(UserMixin, db.Model):

443

id = db.Column(db.Integer, primary_key=True)

444

email = db.Column(db.String(255), unique=True)

445

password = db.Column(db.String(255))

446

password_history = db.Column(db.Text) # JSON field for password history

447

448

def add_password_to_history(self, password_hash):

449

"""Add password hash to history."""

450

history = json.loads(self.password_history or '[]')

451

history.append({

452

'hash': password_hash,

453

'created_at': datetime.utcnow().isoformat()

454

})

455

# Keep only last 5 passwords

456

self.password_history = json.dumps(history[-5:])

457

458

def is_password_in_history(self, password):

459

"""Check if password was used previously."""

460

history = json.loads(self.password_history or '[]')

461

return any(

462

verify_password(password, entry['hash'])

463

for entry in history

464

)

465

466

# Custom password change with history check

467

@app.route('/secure-change-password', methods=['POST'])

468

@login_required

469

def secure_change_password():

470

new_password = request.form.get('new_password')

471

472

if current_user.is_password_in_history(new_password):

473

flash('Cannot reuse a recent password')

474

return redirect('/change-password')

475

476

# Add current password to history before changing

477

current_user.add_password_to_history(current_user.password)

478

current_user.password = hash_password(new_password)

479

db.session.commit()

480

481

flash('Password changed successfully')

482

return redirect('/profile')

483

```

484

485

## Configuration Options

486

487

Key configuration variables for password management:

488

489

```python

490

# Password hashing

491

app.config['SECURITY_PASSWORD_HASH'] = 'bcrypt' # or 'argon2', 'pbkdf2_sha512'

492

app.config['SECURITY_PASSWORD_SALT'] = 'your-secret-salt'

493

494

# Password validation

495

app.config['SECURITY_PASSWORD_LENGTH_MIN'] = 8

496

app.config['SECURITY_PASSWORD_LENGTH_MAX'] = 128

497

app.config['SECURITY_PASSWORD_COMPLEXITY_CHECKER'] = 'zxcvbn'

498

app.config['SECURITY_PASSWORD_CHECK_BREACHED'] = True

499

app.config['SECURITY_PASSWORD_BREACHED_COUNT'] = 1

500

501

# Password reset

502

app.config['SECURITY_RECOVERABLE'] = True

503

app.config['SECURITY_RESET_PASSWORD_WITHIN'] = '5 days'

504

app.config['SECURITY_RESET_URL'] = '/reset-password'

505

506

# Change password

507

app.config['SECURITY_CHANGEABLE'] = True

508

app.config['SECURITY_CHANGE_URL'] = '/change-password'

509

app.config['SECURITY_POST_CHANGE_REDIRECT_ENDPOINT'] = '/profile'

510

511

# Send password change notice

512

app.config['SECURITY_SEND_PASSWORD_CHANGE_EMAIL'] = True

513

app.config['SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL'] = True

514

```