From d597ca88108f775de489fa531b893f1ec37b9fe6 Mon Sep 17 00:00:00 2001 From: Dan Milne Date: Thu, 1 Jan 2026 14:52:24 +1100 Subject: [PATCH] Fix tests --- ..._test.rb => forward_auth_advanced_test.rb} | 5 +- test/integration/webauthn_security_test.rb | 58 +++++++++---------- test/models/user_test.rb | 31 ++++++++++ 3 files changed, 59 insertions(+), 35 deletions(-) rename test/integration/{forward_auth_system_test.rb => forward_auth_advanced_test.rb} (98%) diff --git a/test/integration/forward_auth_system_test.rb b/test/integration/forward_auth_advanced_test.rb similarity index 98% rename from test/integration/forward_auth_system_test.rb rename to test/integration/forward_auth_advanced_test.rb index e40a716..f0026ea 100644 --- a/test/integration/forward_auth_system_test.rb +++ b/test/integration/forward_auth_advanced_test.rb @@ -1,8 +1,7 @@ require "test_helper" -# Note: This file tests API endpoints directly (post/get/assert_response) -# so it should use IntegrationTest, not SystemTestCase -class ForwardAuthSystemTest < ActionDispatch::IntegrationTest +# Advanced integration tests for Forward Auth API +class ForwardAuthAdvancedTest < ActionDispatch::IntegrationTest setup do @user = users(:one) @admin_user = users(:two) diff --git a/test/integration/webauthn_security_test.rb b/test/integration/webauthn_security_test.rb index c1e862b..6942268 100644 --- a/test/integration/webauthn_security_test.rb +++ b/test/integration/webauthn_security_test.rb @@ -54,45 +54,39 @@ class WebauthnSecurityTest < ActionDispatch::IntegrationTest end # ==================== - # USER HANDLE BINDING TESTS + # USER HANDLE SECURITY TESTS # ==================== - test "user handle is properly bound to WebAuthn credential" do - user = User.create!(email_address: "webauthn_handle_test@example.com", password: "password123") + test "WebAuthn challenge includes authenticated user's handle (not another user's)" do + # Create two users + user_a = User.create!(email_address: "usera@example.com", password: "password123") + user_b = User.create!(email_address: "userb@example.com", password: "password123") - # Create a WebAuthn credential with user handle - user_handle = SecureRandom.uuid - credential = user.webauthn_credentials.create!( - external_id: Base64.urlsafe_encode64("fake_credential_id"), - public_key: Base64.urlsafe_encode64("fake_public_key"), - sign_count: 0, - nickname: "Test Key", - user_handle: user_handle - ) + # Generate handles for both users + handle_a = user_a.webauthn_user_handle + handle_b = user_b.webauthn_user_handle - # Verify user handle is associated with the credential - assert_equal user_handle, credential.user_handle + # Sign in as User A + post signin_path, params: {email_address: user_a.email_address, password: "password123"} + assert_response :redirect - user.destroy - end + # Request WebAuthn challenge (for registration) + post webauthn_challenge_path, params: {email: user_a.email_address} + assert_response :success - test "WebAuthn authentication validates user handle" do - user = User.create!(email_address: "webauthn_handle_auth_test@example.com", password: "password123") + # Parse the JSON response + challenge_data = JSON.parse(response.body) - user_handle = SecureRandom.uuid - user.webauthn_credentials.create!( - external_id: Base64.urlsafe_encode64("fake_credential_id"), - public_key: Base64.urlsafe_encode64("fake_public_key"), - sign_count: 0, - nickname: "Test Key", - user_handle: user_handle - ) + # SECURITY: Verify challenge includes User A's handle + assert challenge_data.key?("user") + assert_equal handle_a, challenge_data["user"]["id"], "Challenge should include authenticated user's handle" + assert_equal user_a.email_address, challenge_data["user"]["name"] - # Sign in with WebAuthn - # The implementation should verify the user handle matches - # This test documents the expected behavior + # SECURITY: Verify challenge does NOT include User B's handle + assert_not_equal handle_b, challenge_data["user"]["id"], "Challenge should NOT include another user's handle" - user.destroy + user_a.destroy + user_b.destroy end # ==================== @@ -316,7 +310,7 @@ class WebauthnSecurityTest < ActionDispatch::IntegrationTest test "WebAuthn can be required for authentication" do user = User.create!(email_address: "webauthn_required_test@example.com", password: "password123") - user.update!(webauthn_enabled: true) + user.update!(webauthn_required: true) # Sign in with password should still work post signin_path, params: {email_address: "webauthn_required_test@example.com", password: "password123"} @@ -329,7 +323,7 @@ class WebauthnSecurityTest < ActionDispatch::IntegrationTest test "WebAuthn can be used for passwordless authentication" do user = User.create!(email_address: "webauthn_passwordless_test@example.com", password: "password123") - user.update!(webauthn_enabled: true) + user.update!(webauthn_required: true) user.webauthn_credentials.create!( external_id: Base64.urlsafe_encode64("passwordless_credential"), diff --git a/test/models/user_test.rb b/test/models/user_test.rb index b8bec52..ad8862c 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -319,4 +319,35 @@ class UserTest < ActiveSupport::TestCase # Note: parsed_backup_codes method and legacy tests removed # All users now use BCrypt hashes stored in JSON column + + # WebAuthn user handle tests + test "generates and persists unique webauthn user handle" do + user = User.create!(email_address: "webauthn_test@example.com", password: "password123") + + # User should not have a webauthn_id initially + assert_nil user.webauthn_id + + # Getting the user handle should generate and persist it + handle = user.webauthn_user_handle + assert_not_nil handle + assert_equal 86, handle.length # Base64-urlsafe-encoded 64 bytes (no padding) + + # Reload and verify it was persisted + user.reload + assert_equal handle, user.webauthn_id + + # Subsequent calls should return the same handle (stable) + assert_equal handle, user.webauthn_user_handle + end + + test "webauthn user handles are unique across users" do + user1 = User.create!(email_address: "user1@example.com", password: "password123") + user2 = User.create!(email_address: "user2@example.com", password: "password123") + + handle1 = user1.webauthn_user_handle + handle2 = user2.webauthn_user_handle + + # Each user should get a unique handle + assert_not_equal handle1, handle2 + end end