From d60a01f1601b06421a0928ff8fbc4b32a59d03ca Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Fri, 31 Jul 2026 11:52:03 +0100 Subject: [PATCH 1/3] Keep invitations working when secret key base changes We want to rotate secret key base, but would prefer all the existing teacher invitations to not immediately be invalidated so set up a key rotation. The rotation will mean new tokens will be generated with the new key, but will validate with either. I think this is low risk to keep the old invitations working as even with the secret you would need to know the invitation ids which is not easily guessible. --- config/application.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/application.rb b/config/application.rb index 9009846cc..232e83acb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -80,5 +80,10 @@ class Application < Rails::Application appenders.add(io: $stdout, formatter: :json, application: "editor-api@#{ENV['HEROKU_SLUG_COMMIT'] || 'unknown'}") end end + + config.before_initialize do |app| + previous_secret_key_base = ENV.fetch('PREVIOUS_SECRET_KEY_BASE', nil) + app.message_verifiers.rotate(secret_key_base: previous_secret_key_base) if previous_secret_key_base.present? + end end end From 92fd5fbe17922d318ecea124080c0fa54f4a0874 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Fri, 31 Jul 2026 12:04:07 +0100 Subject: [PATCH 2/3] Remove unused deterministic encryption key We don't use deterministic encryption anywhere - you need to explicitly set it by setting `deterministic: true` when calling `encrypt` or similar. --- .env.example | 1 - .github/workflows/ci.yml | 1 - config/application.rb | 1 - 3 files changed, 3 deletions(-) diff --git a/.env.example b/.env.example index d0dfbf59f..df654703d 100644 --- a/.env.example +++ b/.env.example @@ -43,7 +43,6 @@ PROFILE_API_KEY=test # This has to match the value set in Profile (https://githu # Run bin/rails db:encryption:init to generate values for these if you need to encrypt securely locally. ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=primary-key -ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=deterministic-key ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=derivation-salt EDITOR_ENCRYPTION_KEY=a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789abcdef0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5247ef919..1e300c077 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,6 @@ jobs: REDIS_URL: redis://127.0.0.1:6379/1 HOSTNAME: 127.0.0.1 ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: primary-key - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: deterministic-key ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: derivation-salt EDITOR_ENCRYPTION_KEY: a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789abcdef0 SALESFORCE_CONNECT_HOST: 127.0.0.1 diff --git a/config/application.rb b/config/application.rb index 232e83acb..de919ad93 100644 --- a/config/application.rb +++ b/config/application.rb @@ -66,7 +66,6 @@ class Application < Rails::Application config.generators.system_tests = nil config.active_record.encryption.primary_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') - config.active_record.encryption.deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY') config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT') config.x.subscriptions.pardot_form_handler_url = ENV.fetch('PARDOT_SUBSCRIPTION_URL', '') From c90d4fe67de8a8187184b0549dbd7845168bd865 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Fri, 31 Jul 2026 12:17:52 +0100 Subject: [PATCH 3/3] Support rotating of active record encryption keys Note that the last key is the one that will be used for new records After this is deployed and the new key set up, we will need to resave invitations for them to use the new encryption (or just remove the emails from old ones) --- config/application.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index de919ad93..b4d3b7f3d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -65,7 +65,10 @@ class Application < Rails::Application config.generators.system_tests = nil - config.active_record.encryption.primary_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') + config.active_record.encryption.primary_key = [ + ENV.fetch('PREVIOUS_ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY', nil), + ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') + ].compact config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT') config.x.subscriptions.pardot_form_handler_url = ENV.fetch('PARDOT_SUBSCRIPTION_URL', '')