two_factor_authentication 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba9192cf04aafc95a917e76b6efef8217fb22152
4
- data.tar.gz: fe60bb1323aead63cb3712857e88bf4eaab08cfc
3
+ metadata.gz: 3d6d6636a281220ef8fd5aee19a4c4aaa4129797
4
+ data.tar.gz: 4954c89fa183dcb47c3104bfb14035ceaf6d20f4
5
5
  SHA512:
6
- metadata.gz: 9a3d4c7cd0bb5eb3af1bc322ee3cad89a38bb8316178a9050dc5cce11f006930f3c3b916b41e29a1594f3acbc360814d831c6816e453504c21bd93b00b0834d4
7
- data.tar.gz: e651f87940c8fc7d55b653c010c30da565cd18e2bcfd7708dc12c657dd953199f389aae800973b1a5bbfb6d09fdadf5d7689e3b28fe11403f7df3fcece5f2e15
6
+ metadata.gz: c559cf0c9c2c21519efdf25ce615ca5930dfc657564e4209b77298944ea3a2342414e7772b493db65c657c617641d0af4398a31bca0d3fbf81025e417fb688c2
7
+ data.tar.gz: 7e0d5abb909bb53f04d3d0cb887a4e869944eda656318c7e782562ecdf7bd635c8079d144506b1633e71c83368604b989dd9b4a21f2d8c21cb41d1f2849b3b58
@@ -1,29 +1,28 @@
1
1
  language: ruby
2
2
 
3
3
  env:
4
- - "RAILS_VERSION=4.0"
5
- - "RAILS_VERSION=4.1"
6
4
  - "RAILS_VERSION=4.2"
5
+ - "RAILS_VERSION=5.2"
7
6
  - "RAILS_VERSION=master"
8
7
 
9
8
  rvm:
10
- - 2.1
11
- - 2.2
12
- - 2.3.1
9
+ - 2.3.8
10
+ - 2.4.5
11
+ - 2.5.3
13
12
 
14
13
  matrix:
14
+ fast_finish: true
15
15
  allow_failures:
16
16
  - env: "RAILS_VERSION=master"
17
- exclude:
18
- - rvm: 2.1
19
- env: RAILS_VERSION=master
17
+ include:
20
18
  - rvm: 2.2
21
- env: RAILS_VERSION=master
19
+ env: RAILS_VERSION=4.2
22
20
 
23
21
  before_install:
24
- - gem update bundler
22
+ - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
23
+ - gem install bundler -v '< 2'
25
24
 
26
25
  before_script:
27
- - bundle exec rake app:db:migrate
26
+ - bundle exec rake app:db:setup
28
27
 
29
28
  script: bundle exec rake spec
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ rails = case rails_version
9
9
  when "master"
10
10
  {github: "rails/rails"}
11
11
  when "default"
12
- "~> 4.1"
12
+ "~> 5.2"
13
13
  else
14
14
  "~> #{rails_version}"
15
15
  end
@@ -27,5 +27,4 @@ end
27
27
  group :test do
28
28
  gem 'rack_session_access'
29
29
  gem 'ammeter'
30
- gem 'pry'
31
30
  end
data/README.md CHANGED
@@ -54,7 +54,7 @@ migration in `db/migrate/`, which will add the following columns to your table:
54
54
  #### Manual initial setup
55
55
 
56
56
  If you prefer to set up the model and migration manually, add the
57
- `:two_factor_authentication` option to your existing devise options, such as:
57
+ `:two_factor_authenticatable` option to your existing devise options, such as:
58
58
 
59
59
  ```ruby
60
60
  devise :database_authenticatable, :registerable, :recoverable, :rememberable,
@@ -225,24 +225,24 @@ steps:
225
225
  Open the generated file, and replace its contents with the following:
226
226
  ```ruby
227
227
  class PopulateEncryptedOtpFields < ActiveRecord::Migration
228
- def up
229
- User.reset_column_information
230
-
231
- User.find_each do |user|
232
- user.otp_secret_key = user.read_attribute('otp_secret_key')
233
- user.save!
234
- end
235
- end
236
-
237
- def down
238
- User.reset_column_information
239
-
240
- User.find_each do |user|
241
- user.otp_secret_key = ROTP::Base32.random_base32
242
- user.save!
243
- end
244
- end
245
- end
228
+ def up
229
+ User.reset_column_information
230
+
231
+ User.find_each do |user|
232
+ user.otp_secret_key = user.read_attribute('otp_secret_key')
233
+ user.save!
234
+ end
235
+ end
236
+
237
+ def down
238
+ User.reset_column_information
239
+
240
+ User.find_each do |user|
241
+ user.otp_secret_key = ROTP::Base32.random_base32
242
+ user.save!
243
+ end
244
+ end
245
+ end
246
246
  ```
247
247
 
248
248
  5. Generate a migration to remove the `:otp_secret_key` column:
@@ -263,6 +263,143 @@ after them):
263
263
  bundle exec rake db:rollback STEP=3
264
264
  ```
265
265
 
266
+ #### Critical Security Note! Add before_action to your user registration controllers
267
+
268
+ You should have a file registrations_controller.rb in your controllers folder
269
+ to overwrite/customize user registrations. It should include the lines below, for 2FA protection of user model updates, meaning that users can only access the users/edit page after confirming 2FA fully, not simply by logging in. Otherwise the entire 2FA system can be bypassed!
270
+
271
+ ```ruby
272
+ class RegistrationsController < Devise::RegistrationsController
273
+ before_action :confirm_two_factor_authenticated, except: [:new, :create, :cancel]
274
+
275
+ protected
276
+
277
+ def confirm_two_factor_authenticated
278
+ return if is_fully_authenticated?
279
+
280
+ flash[:error] = t('devise.errors.messages.user_not_authenticated')
281
+ redirect_to user_two_factor_authentication_url
282
+ end
283
+ end
284
+ ```
285
+
286
+ #### Critical Security Note! Add 2FA validation to your custom user actions
287
+
288
+ Make sure you are passing the 2FA secret codes securely and checking for them upon critical user actions, such as API key updates, user email or pgp pubkey updates, or any other changess to private/secure account-related details. Validate the secret during the initial 2FA key/secret verification by the user also, of course.
289
+
290
+ For example, a simple account_controller.rb may look something like this:
291
+
292
+ ```
293
+ require 'json'
294
+
295
+ class AccountController < ApplicationController
296
+ before_action :require_signed_in!
297
+ before_action :authenticate_user!
298
+ respond_to :html, :json
299
+
300
+ def account_API
301
+ resp = {}
302
+ begin
303
+ if(account_params["twoFAKey"] && account_params["twoFASecret"])
304
+ current_user.otp_secret_key = account_params["twoFAKey"]
305
+ if(current_user.authenticate_totp(account_params["twoFASecret"]))
306
+ # user has validated their temporary 2FA code, save it to their account, enable 2FA on this account
307
+ current_user.save!
308
+ resp['success'] = "passed 2FA validation!"
309
+ else
310
+ resp['error'] = "failed 2FA validation!"
311
+ end
312
+ elsif(param[:userAccountStuff] && param[:userAccountWidget])
313
+ #before updating important user account stuff and widgets,
314
+ #check to see that the 2FA secret has also been passed in, and verify it...
315
+ if(account_params["twoFASecret"] && current_user.totp_enabled? && current_user.authenticate_totp(account_params["twoFASecret"]))
316
+ # user has passed 2FA checks, do cool user account stuff here
317
+ ...
318
+ else
319
+ # user failed 2FA check! No cool user stuff happens!
320
+ resp[error] = 'You failed 2FA validation!'
321
+ end
322
+
323
+ ...
324
+ end
325
+ else
326
+ resp['error'] = 'unknown format error, not saved!'
327
+ end
328
+ rescue Exception => e
329
+ puts "WARNING: account api threw error : '#{e}' for user #{current_user.username}"
330
+ #print "error trace: #{e.backtrace}\n"
331
+ resp['error'] = "unanticipated server response"
332
+ end
333
+ render json: resp.to_json
334
+ end
335
+
336
+ def account_params
337
+ params.require(:twoFA).permit(:userAccountStuff, :userAcountWidget, :twoFAKey, :twoFASecret)
338
+ end
339
+ end
340
+ ```
341
+
342
+
266
343
  ### Example App
267
344
 
268
345
  [TwoFactorAuthenticationExample](https://github.com/Houdini/TwoFactorAuthenticationExample)
346
+
347
+
348
+ ### Example user actions
349
+
350
+ to use an ENV VAR for the 2FA encryption key:
351
+
352
+ config.otp_secret_encryption_key = ENV['OTP_SECRET_ENCRYPTION_KEY']
353
+
354
+ to set up TOTP for Google Authenticator for user:
355
+
356
+ ```
357
+ current_user.otp_secret_key = current_user.generate_totp_secret
358
+ current_user.save!
359
+ ```
360
+
361
+ ( encrypted db fields are set upon user model save action,
362
+ rails c access relies on setting env var: OTP_SECRET_ENCRYPTION_KEY )
363
+
364
+ to check if user has input the correct code (from the QR display page)
365
+ before saving the user model:
366
+
367
+ ```
368
+ current_user.authenticate_totp('123456')
369
+ ```
370
+
371
+ additional note:
372
+
373
+ ```
374
+ current_user.otp_secret_key
375
+ ```
376
+
377
+ This returns the OTP secret key in plaintext for the user (if you have set the env var) in the console
378
+ the string used for generating the QR given to the user for their Google Auth is something like:
379
+
380
+ otpauth://totp/LABEL?secret=p6wwetjnkjnrcmpd (example secret used here)
381
+
382
+ where LABEL should be something like "example.com (Username)", which shows up in their GA app to remind them the code is for example.com
383
+
384
+ this returns true or false with an allowed_otp_drift_seconds 'grace period'
385
+
386
+ to set TOTP to DISABLED for a user account:
387
+
388
+ ```
389
+ current_user.second_factor_attempts_count=nil
390
+ current_user.encrypted_otp_secret_key=nil
391
+ current_user.encrypted_otp_secret_key_iv=nil
392
+ current_user.encrypted_otp_secret_key_salt=nil
393
+ current_user.direct_otp=nil
394
+ current_user.direct_otp_sent_at=nil
395
+ current_user.totp_timestamp=nil
396
+ current_user.direct_otp=nil
397
+ current_user.otp_secret_key=nil
398
+ current_user.otp_confirmed=nil
399
+ current_user.save! (if in ruby code instead of console)
400
+ current_user.direct_otp? => false
401
+ current_user.totp_enabled? => false
402
+ ```
403
+
404
+
405
+
@@ -2,7 +2,6 @@ require 'two_factor_authentication/version'
2
2
  require 'devise'
3
3
  require 'active_support/concern'
4
4
  require "active_model"
5
- require "active_record"
6
5
  require "active_support/core_ext/class/attribute_accessors"
7
6
  require "cgi"
8
7
 
@@ -47,7 +46,7 @@ end
47
46
 
48
47
  Devise.add_module :two_factor_authenticatable, :model => 'two_factor_authentication/models/two_factor_authenticatable', :controller => :two_factor_authentication, :route => :two_factor_authentication
49
48
 
50
- require 'two_factor_authentication/orm/active_record'
49
+ require 'two_factor_authentication/orm/active_record' if defined?(ActiveRecord::Base)
51
50
  require 'two_factor_authentication/routes'
52
51
  require 'two_factor_authentication/models/two_factor_authenticatable'
53
52
  require 'two_factor_authentication/rails'
@@ -24,7 +24,7 @@ module TwoFactorAuthentication
24
24
  session["#{scope}_return_to"] = request.original_fullpath if request.get?
25
25
  redirect_to two_factor_authentication_path_for(scope)
26
26
  else
27
- render nothing: true, status: :unauthorized
27
+ head :unauthorized
28
28
  end
29
29
  end
30
30
 
@@ -39,7 +39,10 @@ module Devise
39
39
  drift = options[:drift] || self.class.allowed_otp_drift_seconds
40
40
  raise "authenticate_totp called with no otp_secret_key set" if totp_secret.nil?
41
41
  totp = ROTP::TOTP.new(totp_secret, digits: digits)
42
- new_timestamp = totp.verify_with_drift_and_prior(code, drift, totp_timestamp)
42
+ new_timestamp = totp.verify(
43
+ without_spaces(code),
44
+ drift_ahead: drift, drift_behind: drift, after: totp_timestamp
45
+ )
43
46
  return false unless new_timestamp
44
47
  self.totp_timestamp = new_timestamp
45
48
  true
@@ -103,6 +106,10 @@ module Devise
103
106
 
104
107
  private
105
108
 
109
+ def without_spaces(code)
110
+ code.gsub(/\s/, '')
111
+ end
112
+
106
113
  def random_base10(digits)
107
114
  SecureRandom.random_number(10**digits).to_s.rjust(digits, '0')
108
115
  end
@@ -1,3 +1,5 @@
1
+ require "active_record"
2
+
1
3
  module TwoFactorAuthentication
2
4
  module Orm
3
5
  module ActiveRecord
@@ -1,3 +1,3 @@
1
1
  module TwoFactorAuthentication
2
- VERSION = "2.1.1".freeze
2
+ VERSION = "2.2.0".freeze
3
3
  end
@@ -2,6 +2,14 @@ require 'spec_helper'
2
2
 
3
3
  describe Devise::TwoFactorAuthenticationController, type: :controller do
4
4
  describe 'is_fully_authenticated? helper' do
5
+ def post_code(code)
6
+ if Rails::VERSION::MAJOR >= 5
7
+ post :update, params: { code: code }
8
+ else
9
+ post :update, code: code
10
+ end
11
+ end
12
+
5
13
  before do
6
14
  sign_in
7
15
  end
@@ -9,7 +17,7 @@ describe Devise::TwoFactorAuthenticationController, type: :controller do
9
17
  context 'after user enters valid OTP code' do
10
18
  it 'returns true' do
11
19
  controller.current_user.send_new_otp
12
- post :update, code: controller.current_user.direct_otp
20
+ post_code controller.current_user.direct_otp
13
21
  expect(subject.is_fully_authenticated?).to eq true
14
22
  end
15
23
  end
@@ -24,7 +32,7 @@ describe Devise::TwoFactorAuthenticationController, type: :controller do
24
32
 
25
33
  context 'when user enters an invalid OTP' do
26
34
  it 'returns false' do
27
- post :update, code: '12345'
35
+ post_code '12345'
28
36
 
29
37
  expect(subject.is_fully_authenticated?).to eq false
30
38
  end
@@ -86,6 +86,11 @@ describe Devise::Models::TwoFactorAuthenticatable do
86
86
  expect(do_invoke(code, instance)).to eq(true)
87
87
  end
88
88
 
89
+ it 'authenticates a code entered with a space' do
90
+ code = @totp_helper.totp_code.insert(3, ' ')
91
+ expect(do_invoke(code, instance)).to eq(true)
92
+ end
93
+
89
94
  it 'does not authenticate an old code' do
90
95
  code = @totp_helper.totp_code(1.minutes.ago.to_i)
91
96
  expect(do_invoke(code, instance)).to eq(false)
@@ -133,12 +138,12 @@ describe Devise::Models::TwoFactorAuthenticatable do
133
138
 
134
139
  it "returns uri with user's email" do
135
140
  expect(instance.provisioning_uri).
136
- to match(%r{otpauth://totp/houdini@example.com\?secret=\w{16}})
141
+ to match(%r{otpauth://totp/houdini@example.com\?secret=\w{32}})
137
142
  end
138
143
 
139
144
  it 'returns uri with issuer option' do
140
145
  expect(instance.provisioning_uri('houdini')).
141
- to match(%r{otpauth://totp/houdini\?secret=\w{16}$})
146
+ to match(%r{otpauth://totp/houdini\?secret=\w{32}$})
142
147
  end
143
148
 
144
149
  it 'returns uri with issuer option' do
@@ -150,7 +155,7 @@ describe Devise::Models::TwoFactorAuthenticatable do
150
155
  expect(uri.host).to eq('totp')
151
156
  expect(uri.path).to eq('/Magic:houdini')
152
157
  expect(params['issuer'].shift).to eq('Magic')
153
- expect(params['secret'].shift).to match(/\w{16}/)
158
+ expect(params['secret'].shift).to match(/\w{32}/)
154
159
  end
155
160
  end
156
161
  end
@@ -163,10 +168,10 @@ describe Devise::Models::TwoFactorAuthenticatable do
163
168
  shared_examples 'generate_totp_secret' do |klass|
164
169
  let(:instance) { klass.new }
165
170
 
166
- it 'returns a 16 character string' do
171
+ it 'returns a 32 character string' do
167
172
  secret = instance.generate_totp_secret
168
173
 
169
- expect(secret).to match(/\w{16}/)
174
+ expect(secret).to match(/\w{32}/)
170
175
  end
171
176
  end
172
177
 
@@ -1,4 +1,4 @@
1
- class DeviseCreateUsers < ActiveRecord::Migration
1
+ class DeviseCreateUsers < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table(:users) do |t|
4
4
  ## Database authenticatable
@@ -1,4 +1,4 @@
1
- class TwoFactorAuthenticationAddToUsers < ActiveRecord::Migration
1
+ class TwoFactorAuthenticationAddToUsers < ActiveRecord::Migration[4.2]
2
2
  def up
3
3
  change_table :users do |t|
4
4
  t.string :otp_secret_key
@@ -1,4 +1,4 @@
1
- class AddNickanmeToUsers < ActiveRecord::Migration
1
+ class AddNickanmeToUsers < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  change_table :users do |t|
4
4
  t.column :nickname, :string, limit: 64
@@ -1,4 +1,4 @@
1
- class AddEncryptedColumnsToUser < ActiveRecord::Migration
1
+ class AddEncryptedColumnsToUser < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  add_column :users, :encrypted_otp_secret_key, :string
4
4
  add_column :users, :encrypted_otp_secret_key_iv, :string
@@ -1,4 +1,4 @@
1
- class PopulateOtpColumn < ActiveRecord::Migration
1
+ class PopulateOtpColumn < ActiveRecord::Migration[4.2]
2
2
  def up
3
3
  User.reset_column_information
4
4
 
@@ -1,4 +1,4 @@
1
- class RemoveOtpSecretKeyFromUser < ActiveRecord::Migration
1
+ class RemoveOtpSecretKeyFromUser < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  remove_column :users, :otp_secret_key, :string
4
4
  end
@@ -1,4 +1,4 @@
1
- class DeviseCreateAdmins < ActiveRecord::Migration
1
+ class DeviseCreateAdmins < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table(:admins) do |t|
4
4
  ## Database authenticatable
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # This file is auto-generated from the current state of the database. Instead
3
2
  # of editing this file, please use the migrations feature of Active Record to
4
3
  # incrementally modify your database, and then regenerate this schema definition.
@@ -11,48 +10,46 @@
11
10
  #
12
11
  # It's strongly recommended that you check this file into your version control system.
13
12
 
14
- ActiveRecord::Schema.define(version: 20160209032439) do
13
+ ActiveRecord::Schema.define(version: 2016_02_09_032439) do
15
14
 
16
15
  create_table "admins", force: :cascade do |t|
17
- t.string "email", default: "", null: false
18
- t.string "encrypted_password", default: "", null: false
19
- t.string "reset_password_token"
16
+ t.string "email", default: "", null: false
17
+ t.string "encrypted_password", default: "", null: false
18
+ t.string "reset_password_token"
20
19
  t.datetime "reset_password_sent_at"
21
20
  t.datetime "remember_created_at"
22
- t.integer "sign_in_count", default: 0, null: false
21
+ t.integer "sign_in_count", default: 0, null: false
23
22
  t.datetime "current_sign_in_at"
24
23
  t.datetime "last_sign_in_at"
25
- t.string "current_sign_in_ip"
26
- t.string "last_sign_in_ip"
27
- t.datetime "created_at", null: false
28
- t.datetime "updated_at", null: false
24
+ t.string "current_sign_in_ip"
25
+ t.string "last_sign_in_ip"
26
+ t.datetime "created_at", null: false
27
+ t.datetime "updated_at", null: false
28
+ t.index ["email"], name: "index_admins_on_email", unique: true
29
+ t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
29
30
  end
30
31
 
31
- add_index "admins", ["email"], name: "index_admins_on_email", unique: true
32
- add_index "admins", ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
33
-
34
32
  create_table "users", force: :cascade do |t|
35
- t.string "email", default: "", null: false
36
- t.string "encrypted_password", default: "", null: false
37
- t.string "reset_password_token"
33
+ t.string "email", default: "", null: false
34
+ t.string "encrypted_password", default: "", null: false
35
+ t.string "reset_password_token"
38
36
  t.datetime "reset_password_sent_at"
39
37
  t.datetime "remember_created_at"
40
- t.integer "sign_in_count", default: 0, null: false
38
+ t.integer "sign_in_count", default: 0, null: false
41
39
  t.datetime "current_sign_in_at"
42
40
  t.datetime "last_sign_in_at"
43
- t.string "current_sign_in_ip"
44
- t.string "last_sign_in_ip"
45
- t.datetime "created_at", null: false
46
- t.datetime "updated_at", null: false
47
- t.integer "second_factor_attempts_count", default: 0
48
- t.string "nickname", limit: 64
49
- t.string "encrypted_otp_secret_key"
50
- t.string "encrypted_otp_secret_key_iv"
51
- t.string "encrypted_otp_secret_key_salt"
41
+ t.string "current_sign_in_ip"
42
+ t.string "last_sign_in_ip"
43
+ t.datetime "created_at", null: false
44
+ t.datetime "updated_at", null: false
45
+ t.integer "second_factor_attempts_count", default: 0
46
+ t.string "nickname", limit: 64
47
+ t.string "encrypted_otp_secret_key"
48
+ t.string "encrypted_otp_secret_key_iv"
49
+ t.string "encrypted_otp_secret_key_salt"
50
+ t.index ["email"], name: "index_users_on_email", unique: true
51
+ t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true
52
+ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
52
53
  end
53
54
 
54
- add_index "users", ["email"], name: "index_users_on_email", unique: true
55
- add_index "users", ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true
56
- add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
57
-
58
55
  end
@@ -29,7 +29,7 @@ module AuthenticatedModelHelper
29
29
  end
30
30
 
31
31
  def create_table_for_nonencrypted_user
32
- silence_stream(STDOUT) do
32
+ ActiveRecord::Migration.suppress_messages do
33
33
  ActiveRecord::Schema.define(version: 1) do
34
34
  create_table 'users', force: :cascade do |t|
35
35
  t.string 'email', default: '', null: false
@@ -6,6 +6,6 @@ class TotpHelper
6
6
  end
7
7
 
8
8
  def totp_code(time = Time.now)
9
- ROTP::TOTP.new(@secret_key, digits: @otp_length).at(time, true)
9
+ ROTP::TOTP.new(@secret_key, digits: @otp_length).at(time)
10
10
  end
11
11
  end
@@ -27,13 +27,13 @@ Gem::Specification.new do |s|
27
27
  s.add_runtime_dependency 'rails', '>= 3.1.1'
28
28
  s.add_runtime_dependency 'devise'
29
29
  s.add_runtime_dependency 'randexp'
30
- s.add_runtime_dependency 'rotp', '>= 3.2.0'
30
+ s.add_runtime_dependency 'rotp', '>= 4.0.0'
31
31
  s.add_runtime_dependency 'encryptor'
32
32
 
33
33
  s.add_development_dependency 'bundler'
34
34
  s.add_development_dependency 'rake'
35
35
  s.add_development_dependency 'rspec-rails', '>= 3.0.1'
36
- s.add_development_dependency 'capybara', '2.4.1'
36
+ s.add_development_dependency 'capybara', '~> 2.5'
37
37
  s.add_development_dependency 'pry'
38
38
  s.add_development_dependency 'timecop'
39
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: two_factor_authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitrii Golub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-10 00:00:00.000000000 Z
11
+ date: 2019-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 3.2.0
61
+ version: 4.0.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 3.2.0
68
+ version: 4.0.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: encryptor
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +126,16 @@ dependencies:
126
126
  name: capybara
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - '='
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 2.4.1
131
+ version: '2.5'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - '='
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 2.4.1
138
+ version: '2.5'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: pry
141
141
  requirement: !ruby/object:Gem::Requirement