diff --git a/config/initializers/active_record_encryption.rb b/config/initializers/active_record_encryption.rb index e325fb3..38f7f6f 100644 --- a/config/initializers/active_record_encryption.rb +++ b/config/initializers/active_record_encryption.rb @@ -6,17 +6,23 @@ # - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY # - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY # - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT -Rails.application.config.after_initialize do - # Use env vars if set, otherwise derive from SECRET_KEY_BASE (deterministic) - primary_key = ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'] || Rails.application.key_generator.generate_key('active_record_encryption_primary', 32) - deterministic_key = ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'] || Rails.application.key_generator.generate_key('active_record_encryption_deterministic', 32) - key_derivation_salt = ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT'] || Rails.application.key_generator.generate_key('active_record_encryption_salt', 32) - # Configure Rails 7.1+ ActiveRecord encryption - Rails.application.config.active_record.encryption.primary_key = primary_key - Rails.application.config.active_record.encryption.deterministic_key = deterministic_key - Rails.application.config.active_record.encryption.key_derivation_salt = key_derivation_salt - - # Ensure encryption is enabled - Rails.application.config.active_record.encryption.support_unencrypted_data = false +# Use env vars if set, otherwise derive from SECRET_KEY_BASE (deterministic) +primary_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') do + Rails.application.key_generator.generate_key('active_record_encryption_primary', 32) end +deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY') do + Rails.application.key_generator.generate_key('active_record_encryption_deterministic', 32) +end +key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT') do + Rails.application.key_generator.generate_key('active_record_encryption_salt', 32) +end + +# Configure Rails 7.1+ ActiveRecord encryption +Rails.application.config.active_record.encryption.primary_key = primary_key +Rails.application.config.active_record.encryption.deterministic_key = deterministic_key +Rails.application.config.active_record.encryption.key_derivation_salt = key_derivation_salt + +# Allow unencrypted data for existing records (new/updated records will be encrypted) +# Set to false after all existing encrypted columns have been migrated +Rails.application.config.active_record.encryption.support_unencrypted_data = true diff --git a/test/models/oidc_access_token_test.rb b/test/models/oidc_access_token_test.rb index 9681989..a40082d 100644 --- a/test/models/oidc_access_token_test.rb +++ b/test/models/oidc_access_token_test.rb @@ -163,7 +163,7 @@ class OidcAccessTokenTest < ActiveSupport::TestCase user: users(:alice) ) - assert access_token.plaintext_token.length > auth_code.code.length, + assert access_token.plaintext_token.length > auth_code.plaintext_code.length, "Access tokens should be longer than authorization codes" end diff --git a/test/models/oidc_authorization_code_test.rb b/test/models/oidc_authorization_code_test.rb index 6a90bca..bfa3a17 100644 --- a/test/models/oidc_authorization_code_test.rb +++ b/test/models/oidc_authorization_code_test.rb @@ -25,10 +25,10 @@ class OidcAuthorizationCodeTest < ActiveSupport::TestCase user: users(:alice), redirect_uri: "https://example.com/callback" ) - assert_nil new_code.code + assert_nil new_code.code_hmac assert new_code.save - assert_not_nil new_code.code - assert_match /^[A-Za-z0-9_-]+$/, new_code.code + assert_not_nil new_code.code_hmac + assert_match /^[a-f0-9]{64}$/, new_code.code_hmac # SHA256 hex digest end test "should set expiry before validation on create" do @@ -44,22 +44,22 @@ class OidcAuthorizationCodeTest < ActiveSupport::TestCase assert new_code.expires_at <= 11.minutes.from_now # Allow some variance end - test "should validate presence of code" do - @auth_code.code = nil + test "should validate presence of code_hmac" do + @auth_code.code_hmac = nil assert_not @auth_code.valid? - assert_includes @auth_code.errors[:code], "can't be blank" + assert_includes @auth_code.errors[:code_hmac], "can't be blank" end - test "should validate uniqueness of code" do + test "should validate uniqueness of code_hmac" do @auth_code.save! if @auth_code.changed? duplicate = OidcAuthorizationCode.new( - code: @auth_code.code, + code_hmac: @auth_code.code_hmac, application: applications(:another_app), user: users(:bob), redirect_uri: "https://example.com/callback" ) assert_not duplicate.valid? - assert_includes duplicate.errors[:code], "has already been taken" + assert_includes duplicate.errors[:code_hmac], "has already been taken" end test "should validate presence of redirect_uri" do @@ -178,16 +178,16 @@ class OidcAuthorizationCodeTest < ActiveSupport::TestCase user: users(:alice), redirect_uri: "https://example.com/callback" ) - codes << code.code + codes << code.code_hmac end # All codes should be unique assert_equal codes.length, codes.uniq.length - # All codes should match the expected pattern + # All codes should be SHA256 hex digests codes.each do |code| - assert_match /^[A-Za-z0-9_-]+$/, code - assert_equal 43, code.length # Base64 padding removed + assert_match /^[a-f0-9]{64}$/, code + assert_equal 64, code.length # SHA256 hex digest end end end