Skip to content

Commit 5509d98

Browse files
test: add two-factor test infrastructure
Add test_otp 2FA method that simulates a real extension gem with a simple OTP check. Add UserWithTwoFactor model (ActiveRecord + Mongoid), shared behavior, migration, routes, and challenge view.
1 parent 34d86c2 commit 5509d98

8 files changed

Lines changed: 94 additions & 1 deletion

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
require 'shared_user_with_two_factor'
4+
5+
class UserWithTwoFactor < ActiveRecord::Base
6+
self.table_name = 'users'
7+
include Shim
8+
include SharedUserWithTwoFactor
9+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
require 'shared_user_with_two_factor'
4+
5+
class UserWithTwoFactor
6+
include Mongoid::Document
7+
include Shim
8+
include SharedUserWithTwoFactor
9+
10+
field :username, type: String
11+
field :email, type: String, default: ""
12+
field :encrypted_password, type: String, default: ""
13+
field :otp_secret, type: String
14+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<h2>Two-Factor Authentication</h2>
2+
3+
<%= form_tag(two_factor_path(resource_name), method: :post) do %>
4+
<%= text_field_tag :otp_attempt %>
5+
<%= submit_tag "Verify" %>
6+
<% end %>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
# Test-only two-factor method for integration testing.
4+
# Simulates a real 2FA extension with a simple OTP check.
5+
6+
require 'devise/models/two_factor_authenticatable'
7+
8+
module Devise
9+
module Models
10+
module TestOtp
11+
extend ActiveSupport::Concern
12+
13+
def test_otp_two_factor_enabled?
14+
respond_to?(:otp_secret) && otp_secret.present?
15+
end
16+
end
17+
end
18+
end
19+
20+
module Devise
21+
module Strategies
22+
class TestOtp < Devise::Strategies::TwoFactor
23+
def valid?
24+
params[:otp_attempt].present? &&
25+
session[:devise_two_factor_resource_id].present?
26+
end
27+
28+
def verify_two_factor!(resource)
29+
unless resource.respond_to?(:otp_secret) && params[:otp_attempt] == resource.otp_secret
30+
fail!(:invalid_otp)
31+
return
32+
end
33+
end
34+
end
35+
end
36+
end
37+
38+
Warden::Strategies.add(:test_otp, Devise::Strategies::TestOtp)
39+
40+
Devise.register_two_factor_method :test_otp,
41+
model: 'devise/models/test_otp',
42+
strategy: :test_otp

test/rails_app/config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
# Users scope
2323
devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
2424

25+
devise_for :user_with_two_factors
26+
2527
devise_for :user_on_main_apps,
2628
class_name: 'UserOnMainApp',
2729
router_name: :main_app,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
class AddOtpSecretToUsers < ActiveRecord::Migration[6.0]
4+
def change
5+
add_column :users, :otp_secret, :string
6+
end
7+
end

test/rails_app/db/schema.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#
1414
# It's strongly recommended that you check this file into your version control system.
1515

16-
ActiveRecord::Schema.define(version: 20100401102949) do
16+
ActiveRecord::Schema.define(version: 20260303000000) do
1717

1818
create_table "admins", force: true do |t|
1919
t.string "email"
@@ -50,6 +50,7 @@
5050
t.integer "failed_attempts", default: 0
5151
t.string "unlock_token"
5252
t.datetime "locked_at"
53+
t.string "otp_secret"
5354
t.datetime "created_at"
5455
t.datetime "updated_at"
5556
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
module SharedUserWithTwoFactor
4+
extend ActiveSupport::Concern
5+
6+
included do
7+
devise :database_authenticatable, :registerable, :recoverable,
8+
:two_factor_authenticatable, two_factor_methods: [:test_otp]
9+
10+
validates_uniqueness_of :email, allow_blank: true, if: :devise_will_save_change_to_email?
11+
end
12+
end

0 commit comments

Comments
 (0)