devise-authy 1.11.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4dcb41db3fa90c5055347a840927fe3480f2eb6622499775dac3996f9d3683a
4
- data.tar.gz: e7d144883aa6ac75efb34ef13f1a9145529604d6dc9fc648a2427f9749e8f719
3
+ metadata.gz: 9157eb5b102b0297c975ea71d190f500e0bf6dc5831717df0ca5695b9f0af13e
4
+ data.tar.gz: 7582bfb6b18310dd7697a808460ac87282d1c2e3fc9fd09838c431d69315d655
5
5
  SHA512:
6
- metadata.gz: 89a291d8930edb905bae19949b253b645f7aaa319ff631fe0ec19da5133a363d0fcbbaa54e706e1f1495d8a219f8c4e2925309f9f22cfba94a531fb9413e3425
7
- data.tar.gz: a89caa9abd9fbbcc088175b66949f945602a7758dc120b12a274bd0967ea41d9a42fc13b86561e51dfd269c33bb4174b6b024b499c2d98b93d0e7746b64f6dca
6
+ metadata.gz: 36cc430cf270f535c3068c15668a398bbc9a297d345bb0c659db49ef5fb9febb43761abcf118c340476797a8c7150de37cb6ddcd0caf14b408cdecb94ab856f6
7
+ data.tar.gz: 856434e4038f931c25f84ddac5281f0b5f762e4a6d9d27a355362822342c255bea58ee6b087a52d912802b654436ef7505efc6a310375cafb67bdfafa5d800e1
data/.gitignore CHANGED
@@ -31,14 +31,15 @@ build/
31
31
  Gemfile.lock
32
32
  .ruby-version
33
33
  .ruby-gemset
34
+ gemfiles/*.lock
34
35
 
35
36
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
37
  .rvmrc
37
38
 
38
- spec/rails-app/tmp
39
- spec/rails-app/db
40
- spec/rails-app/log
41
- *.sqlite3DS_Store
39
+ **/*.sqlite
40
+ **/*.log
42
41
 
43
42
  initializers/authy.rb
44
43
  .byebug_history
44
+
45
+ .rspec_status
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --color
2
+ --require ./spec/spec_helper
@@ -1,18 +1,17 @@
1
1
  language: ruby
2
- before_install:
3
- - "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
4
- - rvm @global do gem uninstall bundler -a -x
5
- - rvm @global do yes | gem install bundler -v '< 2.0.0'
6
- - cd spec/rails-app && BUNDLE_GEMFILE=$TRAVIS_BUILD_DIR/spec/rails-app/Gemfile bundle install && cd ../..
7
2
  script: bundle exec rspec
8
3
  rvm:
4
+ - 2.7
9
5
  - 2.6
10
6
  - 2.5
11
7
  - 2.4
12
- - 2.3
13
- - 2.2
14
8
  - ruby-head
9
+ gemfile:
10
+ - gemfiles/rails_5_2.gemfile
11
+ - gemfiles/rails_6.gemfile
15
12
  matrix:
16
13
  allow_failures:
17
14
  - rvm: ruby-head
18
- - rvm: 2.2
15
+ exclude:
16
+ - rvm: 2.4
17
+ gemfile: gemfiles/rails_6.gemfile
@@ -0,0 +1,21 @@
1
+ appraise "rails-5-2" do
2
+ gem "rails", "~> 5.2.0"
3
+ gem "sqlite3", "~> 1.3.13"
4
+
5
+ group :development, :test do
6
+ gem 'factory_girl_rails', :require => false
7
+ gem 'rspec-rails', "~>4.0.0.beta3", :require => false
8
+ gem 'database_cleaner', :require => false
9
+ end
10
+ end
11
+
12
+ appraise "rails-6" do
13
+ gem "rails", "~> 6.0.0"
14
+ gem "sqlite3", "~> 1.4"
15
+
16
+ group :development, :test do
17
+ gem 'factory_girl_rails', :require => false
18
+ gem 'rspec-rails', "~>4.0.0.beta3", :require => false
19
+ gem 'database_cleaner', :require => false
20
+ end
21
+ end if RUBY_VERSION.to_f >= 2.5
@@ -9,6 +9,55 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
9
9
 
10
10
  ...
11
11
 
12
+ ## [2.2.1] - 2020-10-13
13
+
14
+ ### Fixed
15
+
16
+ - If the app offers a QR code scan and user fails to verify authy installation, the QR code wasn't shown again. Fixed in (#149)
17
+
18
+ ## [2.2.0] - 2020-06-04
19
+
20
+ ### Fixed
21
+
22
+ - Don't delete user in Authy if another user has the same authy_id (#144)
23
+
24
+ ## [2.1.0] - 2020-05-05
25
+
26
+ ### Added
27
+
28
+ - Support for generic authenticator tokens (#141)
29
+
30
+ ### Fixed
31
+
32
+ - Can remember device when enabling 2FA for the first time (#139)
33
+
34
+ ## [2.0.0] - 2020-04-28
35
+
36
+ Releasing this as version 2 because there is a significant change in dependencies. Minimum version of Rails is now 5 and of Devise is now 4. Otherwise the gem should work as before.
37
+
38
+ ### Added
39
+
40
+ - HTTP Only flag to remember_device cookie (#116 thanks @agronv)
41
+ - Remembers device when user logs in with One Touch (#128 thanks @cplopez4)
42
+ - Autocomplete attributes for HTML form (#130)
43
+
44
+ ### Changed
45
+
46
+ - Mocked API calls in test suite (#123)
47
+ - Full test suite refactor (#124)
48
+ - Increased required version for Devise and Rails (#125)
49
+ - Stopped calling `signed_in?` before it is needed (#126)
50
+
51
+ ### Fixes
52
+
53
+ - Remembers user correctly when logging in with One Touch (#129)
54
+
55
+ ## [1.11.1] - 2019-02-02
56
+
57
+ ### Fixed
58
+
59
+ - Using the version before loading it broke everything. :facepalm:
60
+
12
61
  ## [1.11.0] - 2019-02-01
13
62
 
14
63
  ### Fixed
data/Gemfile CHANGED
@@ -2,28 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- group :test do
6
- gem 'rails', '~> 4.2.7'
7
- gem 'sqlite3'
8
-
9
- # Use SCSS for stylesheets
10
- gem 'sass-rails', '~> 5.0'
11
-
12
- # Use Uglifier as compressor for JavaScript assets
13
- gem 'uglifier', '>= 1.3.0'
14
-
15
- # Use CoffeeScript for .coffee assets and views
16
- gem 'coffee-rails', '~> 4.1.0'
17
-
18
- # Use jquery as the JavaScript library
19
- gem 'jquery-rails'
20
-
21
- gem 'launchy'
22
- gem 'rspec-rails'
23
- gem 'database_cleaner'
24
- gem 'capybara'
25
- gem 'test-unit'
26
- end
27
-
28
5
  # bundle exec rake doc:rails generates the API under doc/api.
29
6
  gem 'sdoc', '~> 0.4.0', group: :doc
data/README.md CHANGED
@@ -1,10 +1,28 @@
1
1
  # Authy Devise [![Build Status](https://travis-ci.org/twilio/authy-devise.svg?branch=master)](https://travis-ci.org/twilio/authy-devise)
2
2
 
3
- This is a [Devise](https://github.com/plataformatec/devise) extension to add Two-Factor Authentication with Authy to your rails application.
3
+ This is a [Devise](https://github.com/plataformatec/devise) extension to add [Two-Factor Authentication with Authy](https://www.twilio.com/docs/authy) to your Rails application.
4
+
5
+ * [Pre-requisites](#pre-requisites)
6
+ * [Demo](#demo)
7
+ * [Getting started](#getting-started)
8
+ * [Configuring Models](#configuring-models)
9
+ * [With the generator](#with-the-generator)
10
+ * [Manually](#manually)
11
+ * [Final steps](#final-steps)
12
+ * [Custom Views](#custom-views)
13
+ * [Request a phone call](#request-a-phone-call)
14
+ * [Custom Redirect Paths (eg. using modules)](#custom-redirect-paths-eg-using-modules)
15
+ * [I18n](#i18n)
16
+ * [Session variables](#session-variables)
17
+ * [OneTouch support](#onetouch-support)
18
+ * [Generic authenticator token support](#generic-authenticator-token-support)
19
+ * [Rails 5 CSRF protection](#rails-5-csrf-protection)
20
+ * [Running Tests](#running-tests)
21
+ * [Copyright](#copyright)
4
22
 
5
23
  ## Pre-requisites
6
24
 
7
- To use the Authy API you will need a Twilio Account, [sign up for a free account here](https://www.twilio.com/try-twilio).
25
+ To use the Authy API you will need a Twilio Account, [sign up for a free Twilio account here](https://www.twilio.com/try-twilio).
8
26
 
9
27
  Create an [Authy Application in the Twilio console](https://www.twilio.com/console/authy/applications) and take note of the API key.
10
28
 
@@ -42,24 +60,28 @@ You can add devise_authy to your user model in two ways.
42
60
 
43
61
  #### With the generator
44
62
 
45
- This is the easiest way and is recommended. Run the following command:
63
+ Run the following command:
46
64
 
47
65
  ```bash
48
66
  rails g devise_authy [MODEL_NAME]
49
67
  ```
50
68
 
69
+ To support account locking (recommended), you must add `:authy_lockable` to the `devise :authy_authenticatable, ...` configuration in your model as this is not yet supported by the generator.
70
+
51
71
  #### Manually
52
72
 
53
- Add `:authy_authenticatable` to the `devise` options in your Devise user model:
73
+ Add `:authy_authenticatable` and `:authy_lockable` to the `devise` options in your Devise user model:
54
74
 
55
75
  ```ruby
56
- devise :authy_authenticatable, :database_authenticatable
76
+ devise :authy_authenticatable, :authy_lockable, :database_authenticatable, :lockable
57
77
  ```
58
78
 
79
+ (Note, `:authy_lockable` is optional but recommended. It should be used with Devise's own `:lockable` module).
80
+
59
81
  Also add a new migration. For example, if you are adding to the `User` model, use this migration:
60
82
 
61
83
  ```ruby
62
- class DeviseAuthyAddToUsers < ActiveRecord::Migration[5.2]
84
+ class DeviseAuthyAddToUsers < ActiveRecord::Migration[6.0]
63
85
  def self.up
64
86
  change_table :users do |t|
65
87
  t.string :authy_id
@@ -177,6 +199,20 @@ To enable [Authy push authentication](https://www.twilio.com/authy/features/push
177
199
  config.authy_enable_onetouch = true
178
200
  ```
179
201
 
202
+ ## Generic authenticator token support
203
+
204
+ Authy supports other authenticator apps by providing a QR code that your users can scan.
205
+
206
+ > **To use this feature, you need to enable it in your [Twilio Console](https://www.twilio.com/console/authy/applications)**
207
+
208
+ Once you have enabled generic authenticator tokens, you can enable this in devise-authy by modifying the Devise config file `config/initializers/devise.rb` and adding the configuration:
209
+
210
+ ```
211
+ config.authy_enable_qr_code = true
212
+ ```
213
+
214
+ This will display a QR code on the verification screen (you still need to take a user's phone number and country code). If you have implemented your own views, the QR code URL is available on the verification page as `@authy_qr_code`.
215
+
180
216
  ## Rails 5 CSRF protection
181
217
 
182
218
  In Rails 5 `protect_from_forgery` is no longer prepended to the `before_action` chain. If you call `authenticate_user` before `protect_from_forgery` your request will result in a "Can't verify CSRF token authenticity" error.
@@ -205,13 +241,6 @@ Now on the project root run the following commands:
205
241
  $ bundle exec rspec spec/
206
242
  ```
207
243
 
208
- ## Backporting to Rails 3
209
-
210
- While we are not currently supporting Rails 3, there's an active fork that maintains the backwards compatibility.
211
-
212
- https://github.com/gcosta/authy-devise
213
-
214
244
  ## Copyright
215
245
 
216
- Copyright (c) 2012-2020 Authy Inc. See LICENSE.txt for
217
- further details.
246
+ Copyright (c) 2012-2020 Authy Inc. See LICENSE.txt for further details.
@@ -5,17 +5,25 @@ class Devise::DeviseAuthyController < DeviseController
5
5
  prepend_before_action :find_resource_and_require_password_checked, :only => [
6
6
  :GET_verify_authy, :POST_verify_authy, :GET_authy_onetouch_status
7
7
  ]
8
+
9
+ prepend_before_action :check_resource_has_authy_id, :only => [
10
+ :GET_verify_authy_installation, :POST_verify_authy_installation
11
+ ]
12
+
13
+ prepend_before_action :check_resource_not_authy_enabled, :only => [
14
+ :GET_verify_authy_installation, :POST_verify_authy_installation
15
+ ]
16
+
8
17
  prepend_before_action :authenticate_scope!, :only => [
9
- :GET_enable_authy, :POST_enable_authy,
10
- :GET_verify_authy_installation, :POST_verify_authy_installation,
11
- :POST_disable_authy
18
+ :GET_enable_authy, :POST_enable_authy, :GET_verify_authy_installation,
19
+ :POST_verify_authy_installation, :POST_disable_authy
12
20
  ]
21
+
13
22
  include Devise::Controllers::Helpers
14
23
 
15
24
  def GET_verify_authy
16
- @authy_id = @resource.authy_id
17
25
  if resource_class.authy_enable_onetouch
18
- approval_request = send_one_touch_request['approval_request']
26
+ approval_request = send_one_touch_request(@resource.authy_id)['approval_request']
19
27
  @onetouch_uuid = approval_request['uuid'] if approval_request.present?
20
28
  end
21
29
  render :verify_authy
@@ -30,10 +38,8 @@ class Devise::DeviseAuthyController < DeviseController
30
38
  })
31
39
 
32
40
  if token.ok?
33
- remember_device if params[:remember_device].to_i == 1
34
- if session.delete("#{resource_name}_remember_me") == true && @resource.respond_to?(:remember_me=)
35
- @resource.remember_me = true
36
- end
41
+ remember_device(@resource.id) if params[:remember_device].to_i == 1
42
+ remember_user
37
43
  record_authy_authentication
38
44
  respond_with resource, :location => after_sign_in_path_for(@resource)
39
45
  else
@@ -61,13 +67,11 @@ class Devise::DeviseAuthyController < DeviseController
61
67
  if @authy_user.ok?
62
68
  resource.authy_id = @authy_user.id
63
69
  if resource.save
64
- set_flash_message(:notice, :enabled)
70
+ redirect_to [resource_name, :verify_authy_installation] and return
65
71
  else
66
72
  set_flash_message(:error, :not_enabled)
67
73
  redirect_to after_authy_enabled_path_for(resource) and return
68
74
  end
69
-
70
- redirect_to [resource_name, :verify_authy_installation]
71
75
  else
72
76
  set_flash_message(:error, :not_enabled)
73
77
  render :enable_authy
@@ -76,22 +80,39 @@ class Devise::DeviseAuthyController < DeviseController
76
80
 
77
81
  # Disable 2FA
78
82
  def POST_disable_authy
79
- response = Authy::API.delete_user(:id => resource.authy_id)
80
-
81
- if response.ok?
82
- resource.update_attribute(:authy_enabled, false)
83
- resource.update_attribute(:authy_id, nil)
83
+ authy_id = resource.authy_id
84
+ resource.assign_attributes(:authy_enabled => false, :authy_id => nil)
85
+ resource.save(:validate => false)
86
+
87
+ other_resource = resource.class.find_by(:authy_id => authy_id)
88
+ if other_resource
89
+ # If another resource has the same authy_id, do not delete the user from
90
+ # the API.
84
91
  forget_device
85
-
86
92
  set_flash_message(:notice, :disabled)
87
93
  else
88
- set_flash_message(:error, :not_disabled)
94
+ response = Authy::API.delete_user(:id => authy_id)
95
+ if response.ok?
96
+ forget_device
97
+ set_flash_message(:notice, :disabled)
98
+ else
99
+ # If deleting the user from the API fails, set everything back to what
100
+ # it was before.
101
+ # I'm not sure this is a good idea, but it was existing behaviour.
102
+ # Could be changed in a major version bump.
103
+ resource.assign_attributes(:authy_enabled => true, :authy_id => authy_id)
104
+ resource.save(:validate => false)
105
+ set_flash_message(:error, :not_disabled)
106
+ end
89
107
  end
90
-
91
108
  redirect_to after_authy_disabled_path_for(resource)
92
109
  end
93
110
 
94
111
  def GET_verify_authy_installation
112
+ if resource_class.authy_enable_qr_code
113
+ response = Authy::API.request_qr_code(id: resource.authy_id)
114
+ @authy_qr_code = response.qr_code
115
+ end
95
116
  render :verify_authy_installation
96
117
  end
97
118
 
@@ -105,26 +126,34 @@ class Devise::DeviseAuthyController < DeviseController
105
126
  self.resource.authy_enabled = token.ok?
106
127
 
107
128
  if token.ok? && self.resource.save
129
+ remember_device(@resource.id) if params[:remember_device].to_i == 1
108
130
  record_authy_authentication
109
131
  set_flash_message(:notice, :enabled)
110
132
  redirect_to after_authy_verified_path_for(resource)
111
133
  else
134
+ if resource_class.authy_enable_qr_code
135
+ response = Authy::API.request_qr_code(id: resource.authy_id)
136
+ @authy_qr_code = response.qr_code
137
+ end
112
138
  handle_invalid_token :verify_authy_installation, :not_enabled
113
139
  end
114
140
  end
115
141
 
116
142
  def GET_authy_onetouch_status
117
- status = Authy::API.get_request("onetouch/json/approval_requests/#{params[:onetouch_uuid]}")['approval_request']['status']
143
+ response = Authy::OneTouch.approval_request_status(:uuid => params[:onetouch_uuid])
144
+ status = response.dig('approval_request', 'status')
118
145
  case status
119
146
  when 'pending'
120
147
  head 202
121
148
  when 'approved'
149
+ remember_device(@resource.id) if params[:remember_device].to_i == 1
150
+ remember_user
122
151
  record_authy_authentication
123
152
  render json: { redirect: after_sign_in_path_for(@resource) }
124
153
  when 'denied'
125
154
  head :unauthorized
126
155
  else
127
- head :error
156
+ head :internal_server_error
128
157
  end
129
158
  end
130
159
 
@@ -172,6 +201,16 @@ class Devise::DeviseAuthyController < DeviseController
172
201
  end
173
202
  end
174
203
 
204
+ def check_resource_has_authy_id
205
+ redirect_to [resource_name, :enable_authy] if !resource.authy_id
206
+ end
207
+
208
+ def check_resource_not_authy_enabled
209
+ if resource.authy_id && resource.authy_enabled
210
+ redirect_to after_authy_verified_path_for(resource)
211
+ end
212
+ end
213
+
175
214
  protected
176
215
 
177
216
  def after_authy_enabled_path_for(resource)
@@ -202,4 +241,10 @@ class Devise::DeviseAuthyController < DeviseController
202
241
  def after_account_is_locked
203
242
  sign_out_and_redirect @resource
204
243
  end
244
+
245
+ def remember_user
246
+ if session.delete("#{resource_name}_remember_me") == true && @resource.respond_to?(:remember_me=)
247
+ @resource.remember_me = true
248
+ end
249
+ end
205
250
  end
@@ -1,4 +1,22 @@
1
1
  class DeviseAuthy::PasswordsController < Devise::PasswordsController
2
+ ##
3
+ # In the passwords controller a user can update their password using a
4
+ # recovery token. If `Devise.sign_in_after_reset_password` is `true` then the
5
+ # user is signed in immediately with the
6
+ # `Devise::Controllers::SignInOut#sign_in` method. However, if the user has
7
+ # 2FA enabled they should enter their second factor before they are signed in.
8
+ #
9
+ # This method overrides `Devise::Controllers::SignInOut#sign_in` but only
10
+ # within the `Devise::PasswordsController`. If the user needs to verify 2FA
11
+ # then `sign_in` returns `true`. This short circuits the method before it can
12
+ # call `warden.set_user` and log the user in.
13
+ #
14
+ # The user is redirected to `after_resetting_password_path_for(user)` at which
15
+ # point, since the user is not logged in, redirects again to sign in.
16
+ #
17
+ # This doesn't retain the expected behaviour of
18
+ # `Devise.sign_in_after_reset_password`, but is forgivable because this
19
+ # shouldn't be an avenue to bypass 2FA.
2
20
  def sign_in(resource_or_scope, *args)
3
21
  resource = args.last || resource_or_scope
4
22
 
@@ -5,7 +5,7 @@
5
5
  <%= verify_authy_form do %>
6
6
  <legend><%= I18n.t('submit_token_title', {:scope => 'devise'}) %></legend>
7
7
  <%= label_tag 'authy-token' %>
8
- <%= text_field_tag :token, "", :autocomplete => :off, :id => 'authy-token' %>
8
+ <%= text_field_tag :token, "", :autocomplete => "one-time-code", :inputmode => "numeric", :pattern => "[0-9]*", :id => 'authy-token' %>
9
9
  <label>
10
10
  <%= check_box_tag :remember_device %>
11
11
  <span><%= I18n.t('remember_device', {:scope => 'devise'}) %></span>
@@ -25,11 +25,12 @@
25
25
  (function(){
26
26
  var onetouchInterval = setInterval(function(){
27
27
  var onetouchRequest = new XMLHttpRequest();
28
+ var rememberDevice = document.getElementById("remember_device").checked ? '1' : '0';
28
29
  onetouchRequest.addEventListener("load", function(){
29
30
  if(this.status != 202) clearInterval(onetouchInterval);
30
31
  if(this.status == 200) window.location = JSON.parse(this.responseText).redirect;
31
32
  });
32
- onetouchRequest.open("GET", "<%= polymorphic_path [resource_name, :authy_onetouch_status] %>?onetouch_uuid=<%= @onetouch_uuid %>");
33
+ onetouchRequest.open("GET", "<%= polymorphic_path [resource_name, :authy_onetouch_status] %>?remember_device="+rememberDevice+"&onetouch_uuid=<%= @onetouch_uuid %>");
33
34
  onetouchRequest.send();
34
35
  }, 3000);
35
36
  })();
@@ -4,7 +4,7 @@
4
4
  %legend= I18n.t('submit_token_title', {:scope => 'devise'})
5
5
  = hidden_field_tag :"#{resource_name}_id", @resource.id
6
6
  = label_tag 'authy-token'
7
- = text_field_tag :token, "", :autocomplete => :off, :id => 'authy-token'
7
+ = text_field_tag :token, "", :autocomplete => "one-time-code", :inputmode => "numeric", :pattern => "[0-9]*", :id => 'authy-token'
8
8
  %label
9
9
  = check_box_tag :remember_device
10
10
  %span= I18n.t('remember_device', {:scope => 'devise'})
@@ -22,11 +22,12 @@
22
22
  (function(){
23
23
  var onetouchInterval = setInterval(function(){
24
24
  var onetouchRequest = new XMLHttpRequest();
25
+ var rememberDevice = document.getElementById("remember_device").checked ? '1' : '0';
25
26
  onetouchRequest.addEventListener("load", function(){
26
27
  if(this.status != 202) clearInterval(onetouchInterval);
27
28
  if(this.status == 200) window.location = JSON.parse(this.responseText).redirect;
28
29
  });
29
- onetouchRequest.open("GET", "#{polymorphic_path [resource_name, :authy_onetouch_status]}?onetouch_uuid=#{@onetouch_uuid}");
30
+ onetouchRequest.open("GET", "#{polymorphic_path [resource_name, :authy_onetouch_status]}?remember_device="+rememberDevice+"&onetouch_uuid=#{@onetouch_uuid}");
30
31
  onetouchRequest.send();
31
32
  }, 3000);
32
33
  })();
@@ -1,10 +1,18 @@
1
1
  <h2><%= I18n.t('authy_verify_installation_title', {:scope => 'devise'}) %></h2>
2
2
 
3
+ <% if @authy_qr_code %>
4
+ <%= image_tag @authy_qr_code, :size => '256x256', :alt => I18n.t('authy_qr_code_alt', {:scope => 'devise'}) %>
5
+ <p><%= I18n.t('authy_qr_code_instructions', {:scope => 'devise'}) %></p>
6
+ <% end %>
7
+
3
8
  <%= verify_authy_installation_form do %>
4
9
  <legend><%= I18n.t('submit_token_title', {:scope => 'devise'}) %></legend>
5
10
  <%= label_tag :token %>
6
- <%= text_field_tag :token, "", :autocomplete => :off, :id => 'authy-token' %>
11
+ <%= text_field_tag :token, "", :autocomplete => "one-time-code", :inputmode => "numeric", :pattern => "[0-9]*", :id => 'authy-token' %>
12
+ <label>
13
+ <%= check_box_tag :remember_device %>
14
+ <span><%= I18n.t('remember_device', {:scope => 'devise'}) %></span>
15
+ </label>
7
16
  <%= authy_request_sms_link %>
8
17
  <%= submit_tag I18n.t('enable_my_account', {:scope => 'devise'}), :class => 'btn' %>
9
- <% end %>
10
-
18
+ <% end %>
@@ -1,8 +1,16 @@
1
1
  %h2= I18n.t('authy_verify_installation_title', {:scope => 'devise'})
2
+
3
+ - if @authy_qr_code
4
+ = image_tag @authy_qr_code, :size => '256x256', :alt => I18n.t('authy_qr_code_alt', {:scope => 'devise'})
5
+ %p= I18n.t('authy_qr_code_instructions', {:scope => 'devise'})
6
+
2
7
  = verify_authy_installation_form do
3
8
  %legend= I18n.t('submit_token_title', {:scope => 'devise'})
4
9
  = label_tag :token
5
- = text_field_tag :token, "", :autocomplete => :off, :id => 'authy-token'
10
+ = text_field_tag :token, "", :autocomplete => "one-time-code", :inputmode => "numeric", :pattern => "[0-9]*", :id => 'authy-token'
11
+ %label
12
+ = check_box_tag :remember_device
13
+ %span= I18n.t('remember_device', {:scope => 'devise'})
6
14
  = authy_request_sms_link
7
15
  = submit_tag I18n.t('enable_my_account', {:scope => 'devise'}), :class => 'btn'
8
16
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+ require "bundler"
5
+
6
+ Bundler.require :default, :development
7
+
8
+ Combustion.initialize! :all
9
+ run Combustion::Application
@@ -14,6 +14,9 @@ en:
14
14
  authy_verify_installation_title: 'Verify your account'
15
15
  enable_my_account: 'Enable my account'
16
16
 
17
+ authy_qr_code_alt: 'QR code for scanning with your authenticator app.'
18
+ authy_qr_code_instructions: 'Scan this QR code with your authenticator application and enter the code below.'
19
+
17
20
  devise_authy:
18
21
  user:
19
22
  enabled: 'Two factor authentication was enabled'
@@ -12,29 +12,39 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = %q{Authy plugin for Devise.}
14
14
  spec.description = %q{Authy plugin to add two factor authentication to Devise.}
15
- spec.homepage = "https://github.com/authy/authy-devise"
15
+ spec.homepage = "https://github.com/twilio/authy-devise"
16
16
  spec.license = "MIT"
17
17
 
18
18
  spec.metadata = {
19
- "bug_tracker_uri" => "https://github.com/authy/authy-devise/issues",
20
- "change_log_uri" => "https://github.com/authy/authy-devise/blob/master/CHANGELOG.md",
21
- "documentation_uri" => "https://github.com/authy/authy-devise",
22
- "homepage_uri" => "https://github.com/authy/authy-devise",
23
- "source_code_uri" => "https://github.com/authy/authy-devise"
19
+ "bug_tracker_uri" => "https://github.com/twilio/authy-devise/issues",
20
+ "change_log_uri" => "https://github.com/twilio/authy-devise/blob/master/CHANGELOG.md",
21
+ "documentation_uri" => "https://github.com/twilio/authy-devise",
22
+ "homepage_uri" => "https://github.com/twilio/authy-devise",
23
+ "source_code_uri" => "https://github.com/twilio/authy-devise"
24
24
  }
25
25
 
26
26
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
- f.match(%r{^(test|spec|features|authy-devise-demo)/})
27
+ f.match(%r{^(test|spec|features)/})
28
28
  end
29
29
  spec.require_paths = ["lib"]
30
30
 
31
- spec.add_dependency "devise", ">= 3.0.0"
31
+ spec.add_dependency "devise", ">= 4.0.0"
32
32
  spec.add_dependency "authy", ">= 2.7.5"
33
33
 
34
+ spec.add_development_dependency "appraisal", "~> 2.2"
34
35
  spec.add_development_dependency "bundler", ">= 1.16"
35
- spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rake"
37
+ spec.add_development_dependency "combustion", "~> 1.1"
36
38
  spec.add_development_dependency "rspec", "~> 3.0"
39
+ spec.add_development_dependency "rspec-rails"
40
+ spec.add_development_dependency "rails-controller-testing", "~> 1.0"
37
41
  spec.add_development_dependency "yard", "~> 0.9.11"
38
42
  spec.add_development_dependency "rdoc", "~> 4.3.0"
39
- spec.add_development_dependency "simplecov", "~> 0.16.1"
43
+ spec.add_development_dependency "simplecov", "~> 0.17.1"
44
+ spec.add_development_dependency "webmock", "~> 3.7.6"
45
+ spec.add_development_dependency "rails", ">= 5"
46
+ spec.add_development_dependency "sqlite3"
47
+ spec.add_development_dependency "generator_spec"
48
+ spec.add_development_dependency "database_cleaner", "~> 1.7"
49
+ spec.add_development_dependency "factory_bot_rails", "~> 5.1.1"
40
50
  end
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sdoc", "~> 0.4.0", group: :doc
6
+ gem "rails", "~> 5.2.0"
7
+ gem "sqlite3", "~> 1.3.13"
8
+
9
+ group :development, :test do
10
+ gem "factory_girl_rails", require: false
11
+ gem "rspec-rails", "~>4.0.0.beta3", require: false
12
+ gem "database_cleaner", require: false
13
+ end
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sdoc", "~> 0.4.0", group: :doc
6
+ gem "rails", "~> 6.0.0"
7
+ gem "sqlite3", "~> 1.4"
8
+
9
+ group :development, :test do
10
+ gem "factory_girl_rails", require: false
11
+ gem "rspec-rails", "~>4.0.0.beta3", require: false
12
+ gem "database_cleaner", require: false
13
+ end
14
+
15
+ gemspec path: "../"
@@ -3,12 +3,11 @@ require 'active_support/core_ext/integer/time'
3
3
  require 'devise'
4
4
  require 'authy'
5
5
 
6
- Authy.user_agent = "DeviseAuthy/#{DeviseAuthy::VERSION} - #{Authy.user_agent}"
7
-
8
6
  module Devise
9
- mattr_accessor :authy_remember_device, :authy_enable_onetouch
7
+ mattr_accessor :authy_remember_device, :authy_enable_onetouch, :authy_enable_qr_code
10
8
  @@authy_remember_device = 1.month
11
9
  @@authy_enable_onetouch = false
10
+ @@authy_enable_qr_code = false
12
11
  end
13
12
 
14
13
  module DeviseAuthy
@@ -30,5 +29,7 @@ require 'devise-authy/models/authy_authenticatable'
30
29
  require 'devise-authy/models/authy_lockable'
31
30
  require 'devise-authy/version'
32
31
 
32
+ Authy.user_agent = "DeviseAuthy/#{DeviseAuthy::VERSION} - #{Authy.user_agent}"
33
+
33
34
  Devise.add_module :authy_authenticatable, :model => 'devise-authy/models/authy_authenticatable', :controller => :devise_authy, :route => :authy
34
35
  Devise.add_module :authy_lockable, :model => 'devise-authy/models/authy_lockable'
@@ -9,11 +9,11 @@ module DeviseAuthy
9
9
 
10
10
  private
11
11
 
12
- def remember_device
13
- id = @resource.id
12
+ def remember_device(id)
14
13
  cookies.signed[:remember_device] = {
15
14
  :value => {expires: Time.now.to_i, id: id}.to_json,
16
15
  :secure => !(Rails.env.test? || Rails.env.development?),
16
+ :httponly => !(Rails.env.test? || Rails.env.development?),
17
17
  :expires => resource_class.authy_remember_device.from_now
18
18
  }
19
19
  end
@@ -40,7 +40,7 @@ module DeviseAuthy
40
40
  end
41
41
 
42
42
  def is_signing_in?
43
- if devise_controller? && signed_in?(resource_name) &&
43
+ if devise_controller? &&
44
44
  is_devise_sessions_controller? &&
45
45
  self.action_name == "create"
46
46
  return true
@@ -76,8 +76,8 @@ module DeviseAuthy
76
76
  send(:"#{scope}_verify_authy_path")
77
77
  end
78
78
 
79
- def send_one_touch_request
80
- Authy::OneTouch.send_approval_request(id: @authy_id, message: I18n.t('request_to_login', { :scope => 'devise' }))
79
+ def send_one_touch_request(authy_id)
80
+ Authy::OneTouch.send_approval_request(id: authy_id, message: I18n.t('request_to_login', { :scope => 'devise' }))
81
81
  end
82
82
 
83
83
  def record_authy_authentication
@@ -17,7 +17,7 @@ module Devise
17
17
  where(authy_id: authy_id).first
18
18
  end
19
19
 
20
- Devise::Models.config(self, :authy_remember_device, :authy_enable_onetouch)
20
+ Devise::Models.config(self, :authy_remember_device, :authy_enable_onetouch, :authy_enable_qr_code)
21
21
  end
22
22
  end
23
23
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeviseAuthy
4
- VERSION = '1.11.0'
5
- end
4
+ VERSION = '2.2.1'
5
+ end
@@ -11,12 +11,12 @@ module ActiveRecord
11
11
 
12
12
  private
13
13
 
14
- def rails5?
15
- Rails.version.start_with? '5'
14
+ def versioned_migrations?
15
+ Rails::VERSION::MAJOR >= 5
16
16
  end
17
17
 
18
18
  def migration_version
19
- "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if rails5?
19
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if versioned_migrations?
20
20
  end
21
21
  end
22
22
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DeviseAuthy
2
4
  module Generators
3
5
  class DeviseAuthyGenerator < Rails::Generators::NamedBase
@@ -7,7 +9,7 @@ module DeviseAuthy
7
9
  desc "Add :authy_authenticatable directive in the given model, plus accessors. Also generate migration for ActiveRecord"
8
10
 
9
11
  def inject_devise_authy_content
10
- path = File.join("app","models","#{file_path}.rb")
12
+ path = File.join(destination_root, "app","models","#{file_path}.rb")
11
13
  if File.exists?(path) &&
12
14
  !File.read(path).include?("authy_authenticatable")
13
15
  inject_into_file(path,
@@ -19,7 +21,7 @@ module DeviseAuthy
19
21
  !File.read(path).include?(":authy_id")
20
22
  inject_into_file(path,
21
23
  ":authy_id, :last_sign_in_with_authy, ",
22
- :after => "attr_accessible ")
24
+ :after => "attr_accessible ")
23
25
  end
24
26
  end
25
27
 
@@ -1,7 +1,9 @@
1
+ require "rails/generators"
2
+
1
3
  module DeviseAuthy
2
4
  module Generators
3
5
  # Install Generator
4
- class InstallGenerator < Rails::Generators::Base
6
+ class InstallGenerator < ::Rails::Generators::Base
5
7
  source_root File.expand_path("../../templates", __FILE__)
6
8
 
7
9
  class_option :haml, :type => :boolean, :required => false, :default => false, :desc => "Generate views in Haml"
@@ -15,8 +17,10 @@ module DeviseAuthy
15
17
  " # How long should the user's device be remembered for.\n" +
16
18
  " # config.authy_remember_device = 1.month\n\n" +
17
19
  " # Should Authy OneTouch be enabled?\n" +
18
- " # config.authy_enable_onetouch = false\n\n", :after => "Devise.setup do |config|\n"
19
-
20
+ " # config.authy_enable_onetouch = false\n\n" +
21
+ " # Should generating QR codes for other authenticator apps be enabled?\n" +
22
+ " # Note: you need to enable this in your Twilio console.\n" +
23
+ " # config.authy_enable_qr_code = false\n\n", :after => "Devise.setup do |config|\n"
20
24
  end
21
25
 
22
26
  def add_initializer
@@ -61,14 +65,14 @@ module DeviseAuthy
61
65
  @
62
66
  },
63
67
  :erb => {
64
- :before => %r{\s*</\s*head\s*>\s*},
68
+ :before => %r{\s*<\/\s*head\s*>\s*},
65
69
  :content => %@
66
70
  <%=javascript_include_tag "https://www.authy.com/form.authy.min.js" %>
67
71
  <%=stylesheet_link_tag "https://www.authy.com/form.authy.min.css" %>
68
72
  @
69
73
  }
70
74
  }.each do |extension, opts|
71
- file_path = "app/views/layouts/application.html.#{extension}"
75
+ file_path = File.join(destination_root, "app", "views", "layouts", "application.html.#{extension}")
72
76
  if File.exists?(file_path) && !File.read(file_path).include?("form.authy.min.js")
73
77
  inject_into_file(file_path, opts.delete(:content), opts)
74
78
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-authy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Authy Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-31 00:00:00.000000000 Z
11
+ date: 2020-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: devise
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.0.0
19
+ version: 4.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 3.0.0
26
+ version: 4.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: authy
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 2.7.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: appraisal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.2'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -54,18 +68,32 @@ dependencies:
54
68
  version: '1.16'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: combustion
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - "~>"
60
88
  - !ruby/object:Gem::Version
61
- version: '10.0'
89
+ version: '1.1'
62
90
  type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
94
  - - "~>"
67
95
  - !ruby/object:Gem::Version
68
- version: '10.0'
96
+ version: '1.1'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rspec
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +108,34 @@ dependencies:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
110
  version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rails-controller-testing
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
83
139
  - !ruby/object:Gem::Dependency
84
140
  name: yard
85
141
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +170,98 @@ dependencies:
114
170
  requirements:
115
171
  - - "~>"
116
172
  - !ruby/object:Gem::Version
117
- version: 0.16.1
173
+ version: 0.17.1
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 0.17.1
181
+ - !ruby/object:Gem::Dependency
182
+ name: webmock
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 3.7.6
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 3.7.6
195
+ - !ruby/object:Gem::Dependency
196
+ name: rails
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '5'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '5'
209
+ - !ruby/object:Gem::Dependency
210
+ name: sqlite3
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: generator_spec
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: database_cleaner
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '1.7'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '1.7'
251
+ - !ruby/object:Gem::Dependency
252
+ name: factory_bot_rails
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: 5.1.1
118
258
  type: :development
119
259
  prerelease: false
120
260
  version_requirements: !ruby/object:Gem::Requirement
121
261
  requirements:
122
262
  - - "~>"
123
263
  - !ruby/object:Gem::Version
124
- version: 0.16.1
264
+ version: 5.1.1
125
265
  description: Authy plugin to add two factor authentication to Devise.
126
266
  email:
127
267
  - support@authy.com
@@ -133,6 +273,7 @@ files:
133
273
  - ".gitignore"
134
274
  - ".rspec"
135
275
  - ".travis.yml"
276
+ - Appraisals
136
277
  - CHANGELOG.md
137
278
  - Gemfile
138
279
  - LICENSE.txt
@@ -149,8 +290,12 @@ files:
149
290
  - app/views/devise/verify_authy.html.haml
150
291
  - app/views/devise/verify_authy_installation.html.erb
151
292
  - app/views/devise/verify_authy_installation.html.haml
293
+ - config.ru
152
294
  - config/locales/en.yml
153
295
  - devise-authy.gemspec
296
+ - gemfiles/.bundle/config
297
+ - gemfiles/rails_5_2.gemfile
298
+ - gemfiles/rails_6.gemfile
154
299
  - lib/devise-authy.rb
155
300
  - lib/devise-authy/controllers/helpers.rb
156
301
  - lib/devise-authy/controllers/view_helpers.rb
@@ -165,15 +310,15 @@ files:
165
310
  - lib/generators/active_record/templates/migration.rb
166
311
  - lib/generators/devise_authy/devise_authy_generator.rb
167
312
  - lib/generators/devise_authy/install_generator.rb
168
- homepage: https://github.com/authy/authy-devise
313
+ homepage: https://github.com/twilio/authy-devise
169
314
  licenses:
170
315
  - MIT
171
316
  metadata:
172
- bug_tracker_uri: https://github.com/authy/authy-devise/issues
173
- change_log_uri: https://github.com/authy/authy-devise/blob/master/CHANGELOG.md
174
- documentation_uri: https://github.com/authy/authy-devise
175
- homepage_uri: https://github.com/authy/authy-devise
176
- source_code_uri: https://github.com/authy/authy-devise
317
+ bug_tracker_uri: https://github.com/twilio/authy-devise/issues
318
+ change_log_uri: https://github.com/twilio/authy-devise/blob/master/CHANGELOG.md
319
+ documentation_uri: https://github.com/twilio/authy-devise
320
+ homepage_uri: https://github.com/twilio/authy-devise
321
+ source_code_uri: https://github.com/twilio/authy-devise
177
322
  post_install_message:
178
323
  rdoc_options: []
179
324
  require_paths:
@@ -189,7 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
334
  - !ruby/object:Gem::Version
190
335
  version: '0'
191
336
  requirements: []
192
- rubygems_version: 3.0.1
337
+ rubygems_version: 3.0.3
193
338
  signing_key:
194
339
  specification_version: 4
195
340
  summary: Authy plugin for Devise.