From 2bcbeed95143448625eccbbf3a3245a1eec26dce Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Mon, 20 May 2024 16:59:23 +0200
Subject: [PATCH] Add some error handling to OTP secret migration (#30344)

---
 ...80905_migrate_devise_two_factor_secrets.rb | 26 ++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/db/post_migrate/20240307180905_migrate_devise_two_factor_secrets.rb b/db/post_migrate/20240307180905_migrate_devise_two_factor_secrets.rb
index 360e4806d..6194cf9ee 100644
--- a/db/post_migrate/20240307180905_migrate_devise_two_factor_secrets.rb
+++ b/db/post_migrate/20240307180905_migrate_devise_two_factor_secrets.rb
@@ -18,7 +18,13 @@ class MigrateDeviseTwoFactorSecrets < ActiveRecord::Migration[7.1]
     users_with_otp_enabled.find_each do |user|
       # Gets the new value on already-updated users
       # Falls back to legacy value on not-yet-migrated users
-      otp_secret = user.otp_secret
+      otp_secret = begin
+        user.otp_secret
+      rescue OpenSSL::OpenSSLError
+        next if ENV['MIGRATION_IGNORE_INVALID_OTP_SECRET'] == 'true'
+
+        abort_with_decryption_error(user)
+      end
 
       Rails.logger.debug { "Processing #{user.email}" }
 
@@ -36,4 +42,22 @@ class MigrateDeviseTwoFactorSecrets < ActiveRecord::Migration[7.1]
   def users_with_otp_enabled
     MigrationUser.where(otp_required_for_login: true, otp_secret: nil)
   end
+
+  def abort_with_decryption_error(user)
+    abort <<~MESSAGE
+
+      ERROR: Unable to decrypt OTP secret for user #{user.id}.
+
+      This is most likely because you have changed the value of `OTP_SECRET` at some point in
+      time after the user configured 2FA.
+
+      In this case, their OTP secret had already been lost with the change to `OTP_SECRET`, and
+      proceeding with this migration will not make the situation worse.
+
+      Please double-check that you have not accidentally changed `OTP_SECRET` just for this
+      migration, and re-run the migration with `MIGRATION_IGNORE_INVALID_OTP_SECRET=true`.
+
+      Migration aborted.
+    MESSAGE
+  end
 end