From dcb576618b868b888a5b1b31e35491f300ce4050 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Fri, 24 Jul 2026 13:53:13 +0200 Subject: Complete the login only after the second factor Enrolled users get a pending marker instead of a session after the password step; a valid code through the challenge writes the real session via reset_session. otp_required without enrollment funnels into setup everywhere except the enrollment, user, and login machinery. --- test/controllers/otp_challenges_controller_test.rb | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 test/controllers/otp_challenges_controller_test.rb (limited to 'test/controllers/otp_challenges_controller_test.rb') diff --git a/test/controllers/otp_challenges_controller_test.rb b/test/controllers/otp_challenges_controller_test.rb new file mode 100644 index 00000000..470e84a2 --- /dev/null +++ b/test/controllers/otp_challenges_controller_test.rb @@ -0,0 +1,39 @@ +require "test_helper" + +class OtpChallengesControllerTest < ActionController::TestCase + fixtures :users + + def setup + @user = users(:quentin) + @user.update!(:otp_secret => ROTP::Base32.random) + @request.session[:pending_otp_user_id] = @user.id + @request.session[:otp_deadline] = 2.minutes.from_now.to_i + @request.session[:otp_attempts] = 0 + end + + test "a valid code completes the login" do + post :create, params: { :code => ROTP::TOTP.new(@user.otp_secret).now } + assert_equal @user.id, session[:user_id] + assert_nil session[:pending_otp_user_id] + end + + test "a wrong code does not log in" do + post :create, params: { :code => "000000" } + assert_nil session[:user_id] + assert_response :success + end + + test "the pending window expires" do + @request.session[:otp_deadline] = 1.minute.ago.to_i + post :create, params: { :code => ROTP::TOTP.new(@user.otp_secret).now } + assert_nil session[:user_id] + assert_redirected_to login_path + end + + test "attempts are limited" do + 5.times { post :create, params: { :code => "000000" } } + post :create, params: { :code => ROTP::TOTP.new(@user.otp_secret).now } + assert_nil session[:user_id] + assert_redirected_to login_path + end +end -- cgit v1.3