User registation working. Sidebar built. Dashboard built. TOTP enable works - TOTP login works
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled

This commit is contained in:
Dan Milne
2025-10-23 18:07:49 +11:00
parent 256cbe3a48
commit 07e87dbaeb
3 changed files with 106 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
class SessionsController < ApplicationController 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: 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 def new
# Redirect to signup if this is first run # Redirect to signup if this is first run
@@ -23,9 +24,9 @@ class SessionsController < ApplicationController
# Check if TOTP is required # Check if TOTP is required
if user.totp_enabled? if user.totp_enabled?
# TODO: Implement TOTP verification flow # Store user ID in session temporarily for TOTP verification
# For now, reject login if TOTP is enabled session[:pending_totp_user_id] = user.id
redirect_to signin_path, alert: "Two-factor authentication is enabled but not yet implemented. Please contact an administrator." redirect_to totp_verification_path
return return
end end
@@ -34,6 +35,49 @@ class SessionsController < ApplicationController
redirect_to after_authentication_url, notice: "Signed in successfully." redirect_to after_authentication_url, notice: "Signed in successfully."
end 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 def destroy
terminate_session terminate_session
redirect_to signin_path, status: :see_other, notice: "Signed out successfully." redirect_to signin_path, status: :see_other, notice: "Signed out successfully."

View File

@@ -0,0 +1,56 @@
<div class="mx-auto max-w-md">
<div class="bg-white py-8 px-6 shadow rounded-lg sm:px-10">
<div class="mb-8">
<h2 class="text-2xl font-bold text-gray-900">Two-Factor Authentication</h2>
<p class="mt-2 text-sm text-gray-600">
Enter the 6-digit code from your authenticator app to complete sign in.
</p>
</div>
<%= form_with url: totp_verification_path, method: :post, class: "space-y-6" do |form| %>
<div>
<%= label_tag :code, "Verification Code", class: "block text-sm font-medium text-gray-700" %>
<%= text_field_tag :code,
nil,
placeholder: "000000",
maxlength: 8,
required: true,
autofocus: true,
autocomplete: "off",
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-center text-2xl tracking-widest font-mono sm:text-sm" %>
<p class="mt-2 text-xs text-gray-500">
Enter your 6-digit authenticator code or an 8-character backup code
</p>
</div>
<div>
<%= form.submit "Verify",
class: "w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
</div>
<% end %>
<div class="mt-6">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500">Need help?</span>
</div>
</div>
<div class="mt-4 text-center">
<p class="text-sm text-gray-600">
Lost access to your authenticator?
</p>
<p class="mt-1 text-xs text-gray-500">
Contact an administrator for assistance.
</p>
</div>
<div class="mt-4 text-center">
<%= link_to "Cancel and sign in again", signin_path, class: "text-sm text-blue-600 hover:text-blue-500" %>
</div>
</div>
</div>
</div>

View File

@@ -15,6 +15,8 @@ Rails.application.routes.draw do
get "/signin", to: "sessions#new", as: :signin get "/signin", to: "sessions#new", as: :signin
post "/signin", to: "sessions#create" post "/signin", to: "sessions#create"
delete "/signout", to: "sessions#destroy", as: :signout delete "/signout", to: "sessions#destroy", as: :signout
get "/totp-verification", to: "sessions#verify_totp", as: :totp_verification
post "/totp-verification", to: "sessions#verify_totp"
# Authenticated routes # Authenticated routes
root "dashboard#index" root "dashboard#index"