def test_activate(): user_id = db_utils.create_user() tfa_secret = pyotp.random_base32() totp = pyotp.TOTP(tfa_secret) # Failed validation between tfa_secret/tfa_response assert not tfa.activate(user_id, tfa_secret, "000000") # Verify 2FA is not active assert not d.engine.scalar(""" SELECT twofa_secret FROM login WHERE userid = %(userid)s """, userid=user_id) # Validation successful, and tfa_secret written into user's `login` record tfa_response = totp.now() assert tfa.activate(user_id, tfa_secret, tfa_response) # The stored twofa_secret must not be plaintext stored_secret = d.engine.scalar(""" SELECT twofa_secret FROM login WHERE userid = %(userid)s """, userid=user_id) assert tfa_secret != stored_secret # The stored secret must be decryptable to the generated tfa_secret assert tfa_secret == tfa._decrypt_totp_secret(stored_secret)
def tfa_init_verify_post_(request): # Extract parameters from the form verify_checkbox = 'verify' in request.params tfasecret = _get_totp_code_from_session() tfarecoverycodes = _get_recovery_codes_from_session() # Does the user want to proceed with enabling 2FA? if verify_checkbox and tfa.store_recovery_codes(request.userid, tfarecoverycodes): # Strip any spaces from the TOTP code (some authenticators display the digits like '123 456') tfaresponse = request.params['tfaresponse'].replace(' ', '') # TOTP+2FA Secret validates (activate & redirect to status page) if tfa.activate(request.userid, tfasecret, tfaresponse): # Invalidate all other login sessions invalidate_other_sessions(request.userid) # Clean up the stored session variables _cleanup_session() raise HTTPSeeOther(location="/control/2fa/status") # TOTP+2FA Secret did not validate else: return Response( define.webpage(request.userid, "control/2fa/init_verify.html", [tfarecoverycodes.split(','), "2fa"], title="Enable 2FA: Final Step")) # The user didn't check the verification checkbox (despite HTML5's client-side check); regenerate codes & redisplay elif not verify_checkbox: return Response( define.webpage(request.userid, "control/2fa/init_verify.html", [tfarecoverycodes.split(','), "verify"], title="Enable 2FA: Final Step"))
def test_2fa_changes_token(app): user = db_utils.create_user(username='******', password='******') resp = app.get('/') csrf = resp.html.find('html')['data-csrf-token'] assert tfa.store_recovery_codes(user, ','.join(tfa.generate_recovery_codes())) tfa_secret = pyotp.random_base32() totp = pyotp.TOTP(tfa_secret) tfa_response = totp.now() assert tfa.activate(user, tfa_secret, tfa_response) old_cookie = app.cookies['WZL'] resp = app.post('/signin', { 'token': csrf, 'username': '******', 'password': '******' }) new_csrf = resp.html.find('html')['data-csrf-token'] assert app.cookies['WZL'] != old_cookie assert new_csrf != csrf assert not d.engine.scalar( "SELECT EXISTS (SELECT 0 FROM sessions WHERE userid = %(user)s)", user=user) assert d.engine.scalar( "SELECT EXISTS (SELECT 0 FROM sessions WHERE additional_data->'2fa_pwd_auth_userid' = %(user)s::text)", user=user)
def tfa_init_verify_post_(request): # Extract parameters from the form verify_checkbox = 'verify' in request.params tfasecret = _get_totp_code_from_session() tfaresponse = request.params['tfaresponse'] tfarecoverycodes = _get_recovery_codes_from_session() # Does the user want to proceed with enabling 2FA? if verify_checkbox and tfa.store_recovery_codes(request.userid, tfarecoverycodes): # Strip any spaces from the TOTP code (some authenticators display the digits like '123 456') tfaresponse = request.params['tfaresponse'].replace(' ', '') # TOTP+2FA Secret validates (activate & redirect to status page) if tfa.activate(request.userid, tfasecret, tfaresponse): # Invalidate all other login sessions invalidate_other_sessions(request.userid) # Clean up the stored session variables _cleanup_session() raise HTTPSeeOther(location="/control/2fa/status") # TOTP+2FA Secret did not validate else: return Response(define.webpage(request.userid, "control/2fa/init_verify.html", [ tfarecoverycodes.split(','), "2fa" ], title="Enable 2FA: Final Step")) # The user didn't check the verification checkbox (despite HTML5's client-side check); regenerate codes & redisplay elif not verify_checkbox: return Response(define.webpage(request.userid, "control/2fa/init_verify.html", [ tfarecoverycodes.split(','), "verify" ], title="Enable 2FA: Final Step"))
def test_2fa_changes_token(app): user = db_utils.create_user(username='******', password='******') resp = app.get('/') csrf = resp.html.find('html')['data-csrf-token'] assert tfa.store_recovery_codes(user, ','.join(tfa.generate_recovery_codes())) tfa_secret = pyotp.random_base32() totp = pyotp.TOTP(tfa_secret) tfa_response = totp.now() assert tfa.activate(user, tfa_secret, tfa_response) old_cookie = app.cookies['WZL'] resp = app.post('/signin', {'token': csrf, 'username': '******', 'password': '******'}) new_csrf = resp.html.find('html')['data-csrf-token'] assert app.cookies['WZL'] != old_cookie assert new_csrf != csrf assert not d.engine.scalar("SELECT EXISTS (SELECT 0 FROM sessions WHERE userid = %(user)s)", user=user) assert d.engine.scalar("SELECT EXISTS (SELECT 0 FROM sessions WHERE additional_data->'2fa_pwd_auth_userid' = %(user)s::text)", user=user)