or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mddevice-authentication.mdgroup-management.mdhttp-integration.mdindex.mdmfa.mdpassword-management.mdsrp-authentication.mduser-management.md

mfa.mddocs/

0

# Multi-Factor Authentication (MFA)

1

2

Complete multi-factor authentication support for AWS Cognito User Pools, including software token (TOTP) and SMS MFA setup, verification, preference management, and challenge responses. Provides enhanced security through two-factor authentication methods.

3

4

## Capabilities

5

6

### Software Token MFA (TOTP)

7

8

Set up and manage Time-based One-Time Password (TOTP) authentication using authenticator apps like Google Authenticator, Authy, or Microsoft Authenticator.

9

10

```python { .api }

11

def associate_software_token(self) -> str:

12

"""

13

Get the secret code for Software Token MFA setup.

14

15

Returns:

16

str: Base32-encoded secret code for TOTP setup

17

18

Usage:

19

- Display as QR code for authenticator app scanning

20

- Provide as manual entry code for authenticator apps

21

- Secret is unique per user and doesn't change

22

23

Requirements:

24

- User must be authenticated (valid access token)

25

- User pool must have MFA enabled

26

"""

27

28

def verify_software_token(self, code: str, device_name: str = "") -> bool:

29

"""

30

Verify TOTP code to complete Software Token MFA registration.

31

32

Args:

33

code (str): 6-digit TOTP code from authenticator app

34

device_name (str, optional): Friendly name for the device

35

36

Returns:

37

bool: True if verification successful, False otherwise

38

39

Actions:

40

- Validates TOTP code against user's secret

41

- Registers the software token for the user

42

- Enables software token MFA for the account

43

44

Note:

45

Must be called after associate_software_token() to complete setup

46

"""

47

```

48

49

**Usage Example:**

50

51

```python

52

from pycognito import Cognito

53

import qrcode

54

import io

55

56

# User must be authenticated

57

u = Cognito('pool-id', 'client-id', username='user')

58

u.authenticate(password='password')

59

60

# Get TOTP secret

61

secret = u.associate_software_token()

62

print(f"Secret code: {secret}")

63

64

# Generate QR code for easy setup

65

totp_uri = f"otpauth://totp/{u.username}?secret={secret}&issuer=MyApp"

66

qr = qrcode.QRCode()

67

qr.add_data(totp_uri)

68

qr.make()

69

70

print("Scan QR code with authenticator app:")

71

qr.print_ascii()

72

73

# User sets up authenticator and provides first code

74

code = input("Enter 6-digit code from authenticator: ")

75

76

# Verify and complete setup

77

if u.verify_software_token(code, "My Phone"):

78

print("Software Token MFA enabled successfully!")

79

else:

80

print("Verification failed. Please try again.")

81

```

82

83

### MFA Preference Management

84

85

Configure MFA preferences for users, including which methods are enabled and which is preferred.

86

87

```python { .api }

88

def set_user_mfa_preference(self, sms_mfa: bool, software_token_mfa: bool, preferred: str = None) -> None:

89

"""

90

Set MFA preferences for the authenticated user.

91

92

Args:

93

sms_mfa (bool): Enable/disable SMS MFA

94

software_token_mfa (bool): Enable/disable Software Token MFA

95

preferred (str, optional): Preferred method ("SMS", "SOFTWARE_TOKEN", or None)

96

97

Valid combinations:

98

- Both False: Disable MFA entirely

99

- One True: Enable that method as preferred

100

- Both True: Enable both, use preferred parameter to choose default

101

102

Requirements:

103

- User must be authenticated

104

- If enabling SMS MFA, phone number must be verified

105

- If enabling Software Token MFA, must call verify_software_token() first

106

107

Raises:

108

ValueError: If preferred value is invalid

109

"""

110

```

111

112

**Usage Example:**

113

114

```python

115

# Enable only Software Token MFA

116

u.set_user_mfa_preference(

117

sms_mfa=False,

118

software_token_mfa=True,

119

preferred="SOFTWARE_TOKEN"

120

)

121

122

# Enable both methods with SMS as preferred

123

u.set_user_mfa_preference(

124

sms_mfa=True,

125

software_token_mfa=True,

126

preferred="SMS"

127

)

128

129

# Disable MFA entirely

130

u.set_user_mfa_preference(

131

sms_mfa=False,

132

software_token_mfa=False

133

)

134

135

print("MFA preferences updated!")

136

```

137

138

### MFA Challenge Responses

139

140

Handle MFA challenges during authentication by responding with appropriate codes.

141

142

```python { .api }

143

def respond_to_software_token_mfa_challenge(self, code: str, mfa_tokens: dict = None) -> None:

144

"""

145

Respond to Software Token MFA challenge during authentication.

146

147

Args:

148

code (str): 6-digit TOTP code from authenticator app

149

mfa_tokens (dict, optional): MFA tokens from exception (uses instance tokens if not provided)

150

151

Actions:

152

- Validates TOTP code

153

- Completes authentication flow

154

- Sets access, ID, and refresh tokens on success

155

156

Usage:

157

Called after catching SoftwareTokenMFAChallengeException during authenticate()

158

"""

159

160

def respond_to_sms_mfa_challenge(self, code: str, mfa_tokens: dict = None) -> None:

161

"""

162

Respond to SMS MFA challenge during authentication.

163

164

Args:

165

code (str): SMS verification code

166

mfa_tokens (dict, optional): MFA tokens from exception (uses instance tokens if not provided)

167

168

Actions:

169

- Validates SMS code

170

- Completes authentication flow

171

- Sets access, ID, and refresh tokens on success

172

173

Usage:

174

Called after catching SMSMFAChallengeException during authenticate()

175

"""

176

```

177

178

**Usage Example:**

179

180

```python

181

from pycognito import Cognito

182

from pycognito.exceptions import (

183

SoftwareTokenMFAChallengeException,

184

SMSMFAChallengeException

185

)

186

187

u = Cognito('pool-id', 'client-id', username='user')

188

189

try:

190

u.authenticate(password='password')

191

print("Authentication successful!")

192

193

except SoftwareTokenMFAChallengeException as e:

194

print("Software Token MFA required")

195

code = input("Enter 6-digit code from authenticator: ")

196

u.respond_to_software_token_mfa_challenge(code, e.get_tokens())

197

print("MFA authentication successful!")

198

199

except SMSMFAChallengeException as e:

200

print("SMS MFA required")

201

code = input("Enter SMS verification code: ")

202

u.respond_to_sms_mfa_challenge(code, e.get_tokens())

203

print("MFA authentication successful!")

204

```

205

206

## Exception Handling

207

208

MFA operations use specialized exceptions to handle different challenge types.

209

210

```python { .api }

211

class MFAChallengeException(WarrantException):

212

"""Base class for MFA challenge exceptions."""

213

214

def __init__(self, message: str, tokens: dict, *args, **kwargs):

215

"""

216

Args:

217

message (str): Exception message

218

tokens (dict): MFA challenge tokens needed for response

219

"""

220

221

def get_tokens(self) -> dict:

222

"""

223

Get MFA challenge tokens.

224

225

Returns:

226

dict: Tokens needed for MFA challenge response

227

"""

228

229

class SoftwareTokenMFAChallengeException(MFAChallengeException):

230

"""Raised when Software Token (TOTP) MFA is required."""

231

232

class SMSMFAChallengeException(MFAChallengeException):

233

"""Raised when SMS MFA is required."""

234

```

235

236

## Usage Patterns

237

238

### Complete TOTP Setup Flow

239

240

```python

241

def setup_totp_mfa(username, password):

242

"""Complete TOTP MFA setup flow."""

243

u = Cognito('pool-id', 'client-id', username=username)

244

245

# Authenticate user

246

u.authenticate(password=password)

247

248

# Get TOTP secret

249

secret = u.associate_software_token()

250

251

print("Set up your authenticator app:")

252

print(f"Manual entry code: {secret}")

253

print(f"Or scan QR code for: otpauth://totp/{username}?secret={secret}&issuer=MyApp")

254

255

# Wait for user to set up authenticator

256

input("Press Enter after setting up authenticator app...")

257

258

# Verify setup

259

while True:

260

code = input("Enter 6-digit code from authenticator: ")

261

262

if u.verify_software_token(code, "Primary Device"):

263

print("TOTP setup successful!")

264

break

265

else:

266

print("Invalid code. Please try again.")

267

268

# Set as preferred MFA method

269

u.set_user_mfa_preference(

270

sms_mfa=False,

271

software_token_mfa=True,

272

preferred="SOFTWARE_TOKEN"

273

)

274

275

print("TOTP MFA is now enabled and preferred!")

276

277

# Usage

278

setup_totp_mfa('user@example.com', 'password')

279

```

280

281

### MFA-Aware Authentication

282

283

```python

284

def authenticate_with_mfa(username, password):

285

"""Authenticate user with MFA support."""

286

u = Cognito('pool-id', 'client-id', username=username)

287

288

try:

289

# Attempt primary authentication

290

u.authenticate(password=password)

291

print("Authentication successful (no MFA required)")

292

return u

293

294

except SoftwareTokenMFAChallengeException as e:

295

print("TOTP MFA required")

296

297

# Get TOTP code from user

298

code = input("Enter 6-digit code from authenticator: ")

299

300

# Respond to challenge

301

u.respond_to_software_token_mfa_challenge(code, e.get_tokens())

302

print("TOTP MFA authentication successful!")

303

return u

304

305

except SMSMFAChallengeException as e:

306

print("SMS MFA required")

307

print("SMS code sent to your registered phone number")

308

309

# Get SMS code from user

310

code = input("Enter SMS verification code: ")

311

312

# Respond to challenge

313

u.respond_to_sms_mfa_challenge(code, e.get_tokens())

314

print("SMS MFA authentication successful!")

315

return u

316

317

except Exception as e:

318

print(f"Authentication failed: {e}")

319

return None

320

321

# Usage

322

user = authenticate_with_mfa('user@example.com', 'password')

323

if user:

324

print(f"Access token: {user.access_token}")

325

```

326

327

### MFA Configuration Management

328

329

```python

330

def manage_mfa_settings(u):

331

"""Interactive MFA settings management."""

332

333

print("\nCurrent MFA Settings:")

334

print("1. Enable TOTP MFA only")

335

print("2. Enable SMS MFA only")

336

print("3. Enable both (TOTP preferred)")

337

print("4. Enable both (SMS preferred)")

338

print("5. Disable MFA")

339

340

choice = input("Select option (1-5): ")

341

342

if choice == "1":

343

# Setup TOTP if not already done

344

secret = u.associate_software_token()

345

print(f"Setup authenticator with: {secret}")

346

code = input("Enter verification code: ")

347

348

if u.verify_software_token(code):

349

u.set_user_mfa_preference(

350

sms_mfa=False,

351

software_token_mfa=True,

352

preferred="SOFTWARE_TOKEN"

353

)

354

print("TOTP MFA enabled!")

355

356

elif choice == "2":

357

u.set_user_mfa_preference(

358

sms_mfa=True,

359

software_token_mfa=False,

360

preferred="SMS"

361

)

362

print("SMS MFA enabled!")

363

364

elif choice == "3":

365

# Ensure TOTP is set up

366

secret = u.associate_software_token()

367

print(f"Setup authenticator with: {secret}")

368

code = input("Enter verification code: ")

369

370

if u.verify_software_token(code):

371

u.set_user_mfa_preference(

372

sms_mfa=True,

373

software_token_mfa=True,

374

preferred="SOFTWARE_TOKEN"

375

)

376

print("Both MFA methods enabled (TOTP preferred)!")

377

378

elif choice == "4":

379

# Ensure TOTP is set up

380

secret = u.associate_software_token()

381

print(f"Setup authenticator with: {secret}")

382

code = input("Enter verification code: ")

383

384

if u.verify_software_token(code):

385

u.set_user_mfa_preference(

386

sms_mfa=True,

387

software_token_mfa=True,

388

preferred="SMS"

389

)

390

print("Both MFA methods enabled (SMS preferred)!")

391

392

elif choice == "5":

393

u.set_user_mfa_preference(

394

sms_mfa=False,

395

software_token_mfa=False

396

)

397

print("MFA disabled!")

398

399

else:

400

print("Invalid choice")

401

402

# Usage

403

u = Cognito('pool-id', 'client-id', username='user')

404

u.authenticate(password='password')

405

manage_mfa_settings(u)

406

```

407

408

### Production MFA Helper

409

410

```python

411

class MFAHelper:

412

"""Helper class for production MFA handling."""

413

414

def __init__(self, user_pool_id, client_id):

415

self.user_pool_id = user_pool_id

416

self.client_id = client_id

417

418

def authenticate_user(self, username, password, mfa_callback=None):

419

"""

420

Authenticate user with MFA callback support.

421

422

Args:

423

username: Username

424

password: Password

425

mfa_callback: Function to get MFA code (type, tokens) -> code

426

"""

427

u = Cognito(self.user_pool_id, self.client_id, username=username)

428

429

try:

430

u.authenticate(password=password)

431

return u, "SUCCESS"

432

433

except SoftwareTokenMFAChallengeException as e:

434

if mfa_callback:

435

code = mfa_callback("TOTP", e.get_tokens())

436

u.respond_to_software_token_mfa_challenge(code, e.get_tokens())

437

return u, "MFA_SUCCESS"

438

return None, "MFA_REQUIRED_TOTP"

439

440

except SMSMFAChallengeException as e:

441

if mfa_callback:

442

code = mfa_callback("SMS", e.get_tokens())

443

u.respond_to_sms_mfa_challenge(code, e.get_tokens())

444

return u, "MFA_SUCCESS"

445

return None, "MFA_REQUIRED_SMS"

446

447

except Exception as e:

448

return None, f"AUTH_FAILED: {e}"

449

450

def setup_totp(self, username, password, device_name="Device"):

451

"""Setup TOTP MFA for user."""

452

u = Cognito(self.user_pool_id, self.client_id, username=username)

453

u.authenticate(password=password)

454

455

secret = u.associate_software_token()

456

return secret, u

457

458

# Usage

459

def get_mfa_code(mfa_type, tokens):

460

"""Callback to get MFA code from user."""

461

if mfa_type == "TOTP":

462

return input("Enter TOTP code: ")

463

elif mfa_type == "SMS":

464

return input("Enter SMS code: ")

465

466

helper = MFAHelper('pool-id', 'client-id')

467

user, status = helper.authenticate_user('username', 'password', get_mfa_code)

468

469

if status == "SUCCESS":

470

print("Authenticated without MFA")

471

elif status == "MFA_SUCCESS":

472

print("Authenticated with MFA")

473

else:

474

print(f"Authentication failed: {status}")

475

```