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

two-factor.mddocs/

0

# Two-Factor Authentication

1

2

TOTP-based two-factor authentication, SMS support, recovery codes, backup authentication methods, and multi-factor security for enhanced account protection in Flask applications.

3

4

## Capabilities

5

6

### Two-Factor Authentication Forms

7

8

Forms for setting up and verifying two-factor authentication.

9

10

```python { .api }

11

class TwoFactorSetupForm(Form):

12

"""

13

Form for setting up two-factor authentication.

14

15

Fields:

16

- setup: Choice field for 2FA method selection

17

- phone: Phone number field (for SMS)

18

- submit: Submit button

19

"""

20

setup: SelectField

21

phone: StringField

22

submit: SubmitField

23

24

class TwoFactorVerifyCodeForm(Form):

25

"""

26

Form for verifying two-factor authentication codes.

27

28

Fields:

29

- code: Code verification field

30

- submit: Submit button

31

"""

32

code: StringField

33

submit: SubmitField

34

35

class TwoFactorRescueForm(Form):

36

"""

37

Form for two-factor authentication recovery.

38

39

Fields:

40

- help_setup: Choice field for recovery method

41

- submit: Submit button

42

"""

43

help_setup: SelectField

44

submit: SubmitField

45

46

class TwoFactorSelectForm(Form):

47

"""

48

Form for selecting two-factor authentication method.

49

50

Fields:

51

- which: Choice field for method selection

52

- submit: Submit button

53

"""

54

which: SelectField

55

submit: SubmitField

56

```

57

58

### Two-Factor Token Functions

59

60

Functions for sending and managing two-factor authentication tokens.

61

62

```python { .api }

63

def tf_send_security_token(user, method, phone_number=None, **kwargs):

64

"""

65

Send two-factor security token to user via specified method.

66

67

Parameters:

68

- user: User object to send token to

69

- method: Delivery method ('sms', 'google-authenticator', 'mail')

70

- phone_number: Phone number for SMS delivery (optional)

71

- kwargs: Additional method-specific parameters

72

73

Returns:

74

True if token sent successfully, False otherwise

75

"""

76

```

77

78

### TOTP (Time-based One-Time Password) Utility

79

80

Class for managing TOTP-based two-factor authentication using authenticator apps.

81

82

```python { .api }

83

class Totp:

84

"""

85

Time-based One-Time Password utility for authenticator app integration.

86

"""

87

88

def __init__(self, secrets=None):

89

"""

90

Initialize TOTP utility.

91

92

Parameters:

93

- secrets: Secret key for TOTP generation (optional)

94

"""

95

96

def generate_password(self, timestamp=None):

97

"""

98

Generate TOTP password for current time.

99

100

Parameters:

101

- timestamp: Custom timestamp (optional, defaults to current time)

102

103

Returns:

104

6-digit TOTP code as string

105

"""

106

107

def verify(self, token, window=0, timestamp=None):

108

"""

109

Verify TOTP token against expected value.

110

111

Parameters:

112

- token: TOTP token to verify

113

- window: Time window for verification (default: 0)

114

- timestamp: Custom timestamp (optional)

115

116

Returns:

117

True if token is valid, False otherwise

118

"""

119

120

def get_totp_uri(self, username, issuer_name=None):

121

"""

122

Generate TOTP URI for QR code generation.

123

124

Parameters:

125

- username: Username for TOTP account

126

- issuer_name: Name of issuing service (optional)

127

128

Returns:

129

TOTP URI string for QR code

130

"""

131

132

@property

133

def secrets(self):

134

"""Get TOTP secret key."""

135

136

@secrets.setter

137

def secrets(self, value):

138

"""Set TOTP secret key."""

139

```

140

141

### Recovery Codes Utility

142

143

Class for managing multi-factor recovery codes as backup authentication method.

144

145

```python { .api }

146

class MfRecoveryCodesUtil:

147

"""

148

Multi-factor recovery codes utility for backup authentication.

149

"""

150

151

def __init__(self, app=None):

152

"""Initialize recovery codes utility."""

153

154

def generate_codes(self, user, count=10):

155

"""

156

Generate recovery codes for user.

157

158

Parameters:

159

- user: User object to generate codes for

160

- count: Number of codes to generate (default: 10)

161

162

Returns:

163

List of recovery code strings

164

"""

165

166

def verify_code(self, user, code):

167

"""

168

Verify and consume recovery code.

169

170

Parameters:

171

- user: User object

172

- code: Recovery code to verify

173

174

Returns:

175

True if code is valid and consumed, False otherwise

176

"""

177

178

def get_remaining_codes(self, user):

179

"""

180

Get count of remaining unused recovery codes.

181

182

Parameters:

183

- user: User object

184

185

Returns:

186

Number of remaining recovery codes

187

"""

188

```

189

190

### Recovery Code Forms

191

192

Forms for managing recovery codes.

193

194

```python { .api }

195

class MfRecoveryCodesForm(Form):

196

"""

197

Form for generating and displaying recovery codes.

198

199

Fields:

200

- submit: Submit button for code generation

201

"""

202

submit: SubmitField

203

204

class MfRecoveryForm(Form):

205

"""

206

Form for recovery code authentication.

207

208

Fields:

209

- code: Recovery code field

210

- submit: Submit button

211

"""

212

code: StringField

213

submit: SubmitField

214

```

215

216

### Two-Factor Plugin System

217

218

Base classes for extending two-factor authentication with custom methods.

219

220

```python { .api }

221

class TfPluginBase:

222

"""

223

Base class for two-factor authentication plugins.

224

"""

225

226

def get_setup_form(self):

227

"""Return setup form for this 2FA method."""

228

229

def setup_tf(self, user, setup_data):

230

"""Setup two-factor authentication for user."""

231

232

def send_token(self, user):

233

"""Send authentication token to user."""

234

235

def verify_token(self, user, token):

236

"""Verify authentication token."""

237

238

def is_setup(self, user):

239

"""Check if 2FA is set up for user."""

240

241

def remove_tf(self, user):

242

"""Remove 2FA setup for user."""

243

244

class TfPlugin:

245

"""

246

Two-factor authentication plugin manager.

247

"""

248

249

def __init__(self, app=None):

250

"""Initialize plugin manager."""

251

252

def register_plugin(self, name, plugin_class):

253

"""Register a 2FA plugin."""

254

255

def get_plugin(self, name):

256

"""Get registered plugin by name."""

257

```

258

259

## Usage Examples

260

261

### Basic Two-Factor Setup

262

263

```python

264

# Enable two-factor authentication

265

app.config['SECURITY_TWO_FACTOR'] = True

266

app.config['SECURITY_TWO_FACTOR_REQUIRED'] = False # Optional by default

267

app.config['SECURITY_TWO_FACTOR_SMS_SERVICE'] = 'Dummy' # For development

268

269

security = Security(app, user_datastore)

270

```

271

272

### TOTP Authenticator App Setup

273

274

```python

275

from flask_security import Totp

276

277

@app.route('/setup-authenticator')

278

@login_required

279

def setup_authenticator():

280

if current_user.tf_totp_secret:

281

flash('Two-factor authentication is already set up')

282

return redirect('/profile')

283

284

# Generate new TOTP secret

285

totp = Totp()

286

current_user.tf_totp_secret = totp.secrets

287

288

# Generate QR code URI

289

qr_uri = totp.get_totp_uri(

290

username=current_user.email,

291

issuer_name='My App'

292

)

293

294

db.session.commit()

295

296

return render_template('setup_totp.html',

297

secret=totp.secrets,

298

qr_uri=qr_uri)

299

300

@app.route('/verify-totp', methods=['POST'])

301

@login_required

302

def verify_totp():

303

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

304

305

if current_user.tf_totp_secret:

306

totp = Totp(current_user.tf_totp_secret)

307

if totp.verify(code, window=1):

308

current_user.tf_primary_method = 'authenticator'

309

db.session.commit()

310

flash('Two-factor authentication enabled successfully')

311

return redirect('/profile')

312

313

flash('Invalid verification code')

314

return redirect('/setup-authenticator')

315

```

316

317

### SMS Two-Factor Authentication

318

319

```python

320

from flask_security import tf_send_security_token

321

322

@app.route('/setup-sms-2fa', methods=['POST'])

323

@login_required

324

def setup_sms_2fa():

325

phone_number = request.form.get('phone')

326

327

# Validate and save phone number

328

current_user.tf_phone_number = phone_number

329

current_user.tf_primary_method = 'sms'

330

331

# Send verification code

332

if tf_send_security_token(current_user, 'sms', phone_number=phone_number):

333

flash('Verification code sent to your phone')

334

return redirect('/verify-sms-2fa')

335

else:

336

flash('Failed to send verification code')

337

return redirect('/setup-2fa')

338

339

@app.route('/verify-sms-2fa', methods=['POST'])

340

@login_required

341

def verify_sms_2fa():

342

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

343

344

# Verify SMS code (implementation depends on SMS service)

345

if verify_sms_code(current_user, code):

346

db.session.commit()

347

flash('SMS two-factor authentication enabled')

348

return redirect('/profile')

349

else:

350

flash('Invalid verification code')

351

return redirect('/verify-sms-2fa')

352

```

353

354

### Recovery Codes Setup

355

356

```python

357

from flask_security import MfRecoveryCodesUtil

358

359

@app.route('/generate-recovery-codes')

360

@login_required

361

def generate_recovery_codes():

362

if not current_user.has_tf_setup():

363

flash('Set up two-factor authentication first')

364

return redirect('/setup-2fa')

365

366

recovery_util = MfRecoveryCodesUtil()

367

codes = recovery_util.generate_codes(current_user, count=10)

368

369

return render_template('recovery_codes.html', codes=codes)

370

371

@app.route('/verify-recovery-code', methods=['POST'])

372

def verify_recovery_code():

373

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

374

user = get_user_from_session() # Get user being verified

375

376

recovery_util = MfRecoveryCodesUtil()

377

if recovery_util.verify_code(user, code):

378

# Log user in and mark as verified

379

login_user(user)

380

flash('Logged in using recovery code')

381

return redirect('/profile')

382

else:

383

flash('Invalid recovery code')

384

return redirect('/2fa-verify')

385

```

386

387

### Custom Two-Factor Plugin

388

389

```python

390

from flask_security import TfPluginBase

391

392

class EmailTfPlugin(TfPluginBase):

393

"""Custom email-based 2FA plugin."""

394

395

def setup_tf(self, user, setup_data):

396

"""Setup email 2FA for user."""

397

user.tf_email_enabled = True

398

return True

399

400

def send_token(self, user):

401

"""Send 2FA code via email."""

402

code = generate_random_code(6)

403

user.tf_email_code = code

404

user.tf_email_code_expires = datetime.utcnow() + timedelta(minutes=5)

405

406

send_mail(

407

subject='Your verification code',

408

recipient=user.email,

409

template='email_2fa_code.html',

410

code=code

411

)

412

return True

413

414

def verify_token(self, user, token):

415

"""Verify email 2FA code."""

416

if (user.tf_email_code == token and

417

user.tf_email_code_expires > datetime.utcnow()):

418

# Clear used code

419

user.tf_email_code = None

420

user.tf_email_code_expires = None

421

return True

422

return False

423

424

def is_setup(self, user):

425

"""Check if email 2FA is set up."""

426

return getattr(user, 'tf_email_enabled', False)

427

428

# Register custom plugin

429

tf_plugin = TfPlugin(app)

430

tf_plugin.register_plugin('email', EmailTfPlugin())

431

```

432

433

### Enforcing Two-Factor Authentication

434

435

```python

436

from flask_security import current_user

437

from functools import wraps

438

439

def tf_required(f):

440

"""Decorator to require two-factor authentication."""

441

@wraps(f)

442

def decorated_function(*args, **kwargs):

443

if not current_user.is_authenticated:

444

return redirect(url_for_security('login'))

445

446

if not current_user.has_tf_setup():

447

flash('Two-factor authentication is required')

448

return redirect('/setup-2fa')

449

450

if not session.get('tf_verified'):

451

return redirect('/2fa-verify')

452

453

return f(*args, **kwargs)

454

return decorated_function

455

456

@app.route('/admin-panel')

457

@tf_required

458

def admin_panel():

459

return render_template('admin.html')

460

```

461

462

### Two-Factor Authentication in API

463

464

```python

465

from flask_security import auth_required

466

467

@app.route('/api/sensitive-data')

468

@auth_required('token')

469

def sensitive_api_data():

470

# Check if user has 2FA enabled and verified

471

if current_user.has_tf_setup() and not session.get('tf_verified'):

472

return jsonify({

473

'error': 'Two-factor authentication required',

474

'tf_required': True,

475

'tf_methods': current_user.get_tf_methods()

476

}), 403

477

478

return jsonify({'data': 'sensitive information'})

479

480

@app.route('/api/verify-2fa', methods=['POST'])

481

@auth_required('token')

482

def api_verify_2fa():

483

code = request.json.get('code')

484

method = request.json.get('method', 'authenticator')

485

486

if method == 'authenticator' and current_user.tf_totp_secret:

487

totp = Totp(current_user.tf_totp_secret)

488

if totp.verify(code, window=1):

489

session['tf_verified'] = True

490

return jsonify({'success': True})

491

492

return jsonify({'error': 'Invalid verification code'}), 400

493

```

494

495

## Configuration Options

496

497

Key configuration variables for two-factor authentication:

498

499

```python

500

# Two-factor authentication

501

app.config['SECURITY_TWO_FACTOR'] = True

502

app.config['SECURITY_TWO_FACTOR_REQUIRED'] = False

503

app.config['SECURITY_TWO_FACTOR_SMS_SERVICE'] = 'Dummy' # or 'Twilio', etc.

504

505

# TOTP settings

506

app.config['SECURITY_TOTP_SECRETS'] = {'1': 'secret-key-here'}

507

app.config['SECURITY_TOTP_ISSUER'] = 'My Application'

508

509

# SMS settings

510

app.config['SECURITY_SMS_SERVICE'] = 'Dummy'

511

app.config['SECURITY_SMS_SERVICE_CONFIG'] = {}

512

513

# Recovery codes

514

app.config['SECURITY_RECOVERY_CODES'] = True

515

app.config['SECURITY_RECOVERY_CODES_N'] = 10

516

517

# Two-factor URLs

518

app.config['SECURITY_TWO_FACTOR_SETUP_URL'] = '/tf-setup'

519

app.config['SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL'] = '/tf-validate'

520

app.config['SECURITY_TWO_FACTOR_RESCUE_URL'] = '/tf-rescue'

521

```