diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index ebc5fa8..202e93b 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,6 +1,7 @@ class SessionsController < ApplicationController - allow_unauthenticated_access only: %i[ new create ] + allow_unauthenticated_access only: %i[ new create verify_totp ] rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to signin_path, alert: "Too many attempts. Try again later." } + rate_limit to: 5, within: 3.minutes, only: :verify_totp, with: -> { redirect_to totp_verification_path, alert: "Too many attempts. Try again later." } def new # Redirect to signup if this is first run @@ -23,9 +24,9 @@ class SessionsController < ApplicationController # Check if TOTP is required if user.totp_enabled? - # TODO: Implement TOTP verification flow - # For now, reject login if TOTP is enabled - redirect_to signin_path, alert: "Two-factor authentication is enabled but not yet implemented. Please contact an administrator." + # Store user ID in session temporarily for TOTP verification + session[:pending_totp_user_id] = user.id + redirect_to totp_verification_path return end @@ -34,6 +35,49 @@ class SessionsController < ApplicationController redirect_to after_authentication_url, notice: "Signed in successfully." end + def verify_totp + # Get the pending user from session + user_id = session[:pending_totp_user_id] + unless user_id + redirect_to signin_path, alert: "Session expired. Please sign in again." + return + end + + user = User.find_by(id: user_id) + unless user + session.delete(:pending_totp_user_id) + redirect_to signin_path, alert: "Session expired. Please sign in again." + return + end + + # Handle form submission + if request.post? + code = params[:code]&.strip + + # Try TOTP verification first + if user.verify_totp(code) + session.delete(:pending_totp_user_id) + start_new_session_for user + redirect_to after_authentication_url, notice: "Signed in successfully." + return + end + + # Try backup code verification + if user.verify_backup_code(code) + session.delete(:pending_totp_user_id) + start_new_session_for user + redirect_to after_authentication_url, notice: "Signed in successfully using backup code." + return + end + + # Invalid code + redirect_to totp_verification_path, alert: "Invalid verification code. Please try again." + return + end + + # Just render the form + end + def destroy terminate_session redirect_to signin_path, status: :see_other, notice: "Signed out successfully." diff --git a/app/views/sessions/verify_totp.html.erb b/app/views/sessions/verify_totp.html.erb new file mode 100644 index 0000000..012540b --- /dev/null +++ b/app/views/sessions/verify_totp.html.erb @@ -0,0 +1,56 @@ +
+ Enter the 6-digit code from your authenticator app to complete sign in. +
++ Enter your 6-digit authenticator code or an 8-character backup code +
++ Lost access to your authenticator? +
++ Contact an administrator for assistance. +
+