two_factor_authentication 0.2 → 1.0
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 +7 -0
- data/.gitignore +1 -0
- data/README.md +32 -19
- data/app/controllers/devise/two_factor_authentication_controller.rb +3 -3
- data/app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb +2 -2
- data/config/locales/en.yml +1 -1
- data/lib/generators/active_record/templates/migration.rb +9 -2
- data/lib/two_factor_authentication.rb +8 -5
- data/lib/two_factor_authentication/controllers/helpers.rb +11 -4
- data/lib/two_factor_authentication/hooks/two_factor_authenticatable.rb +1 -4
- data/lib/two_factor_authentication/models/two_factor_authenticatable.rb +50 -12
- data/lib/two_factor_authentication/schema.rb +2 -2
- data/lib/two_factor_authentication/version.rb +1 -1
- data/spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb +70 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/authenticated_model_helper.rb +29 -0
- data/two_factor_authentication.gemspec +2 -0
- metadata +56 -29
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 39274f1841e29f847f6c2900a9228493e6103243
|
4
|
+
data.tar.gz: c0c3e1aebe06d8981301dfa2ccd85ab6f347b6e0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e4bf30aa6f90afd6ee31400b4acd5e75dfc6fa49162b9ceb18042e406c55b956a0841466b9881c289b842870cb56ca985005e639578861259862c9c04209e3c
|
7
|
+
data.tar.gz: 91f57824cfc4ea2588cd7658c50eb8ee0b221b6dff7c45362d2d2542c51911c61f3f88483e9d79951d14c1c61fb3a4c80a11065bf77a513a0cc5f15d41bd713f
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -32,6 +32,20 @@ Finally, run the migration with:
|
|
32
32
|
|
33
33
|
bundle exec rake db:migrate
|
34
34
|
|
35
|
+
Add the following line to your model to fully enable two-factor auth:
|
36
|
+
|
37
|
+
has_one_time_password
|
38
|
+
|
39
|
+
Set config values if desired for maximum second factor attempts count and allowed time drift for one-time passwords:
|
40
|
+
|
41
|
+
config.max_login_attempts = 3
|
42
|
+
config.allowed_otp_drift_seconds = 30
|
43
|
+
|
44
|
+
Override the method to send one-time passwords in your model, this is automatically called when a user logs in:
|
45
|
+
|
46
|
+
def send_two_factor_authentication_code
|
47
|
+
# use Model#otp_code and send via SMS, etc.
|
48
|
+
end
|
35
49
|
|
36
50
|
### Manual installation
|
37
51
|
|
@@ -42,23 +56,22 @@ To manually enable two factor authentication for the User model, you should add
|
|
42
56
|
:recoverable, :rememberable, :trackable, :validatable, :two_factor_authenticatable
|
43
57
|
```
|
44
58
|
|
45
|
-
|
59
|
+
Add the following line to your model to fully enable two-factor auth:
|
46
60
|
|
47
|
-
|
48
|
-
config.login_code_random_pattern = /\w+/
|
49
|
-
config.max_login_attempts = 3
|
50
|
-
```
|
61
|
+
has_one_time_password
|
51
62
|
|
52
|
-
|
63
|
+
Set config values if desired for maximum second factor attempts count and allowed time drift for one-time passwords:
|
53
64
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
65
|
+
config.max_login_attempts = 3
|
66
|
+
config.allowed_otp_drift_seconds = 30
|
67
|
+
|
68
|
+
Override the method to send one-time passwords in your model, this is automatically called when a user logs in:
|
58
69
|
|
59
|
-
|
70
|
+
def send_two_factor_authentication_code
|
71
|
+
# use Model#otp_code and send via SMS, etc.
|
72
|
+
end
|
60
73
|
|
61
|
-
### Customisation
|
74
|
+
### Customisation and Usage
|
62
75
|
|
63
76
|
By default second factor authentication enabled for each user, you can change it with this method in your User model:
|
64
77
|
|
@@ -70,12 +83,12 @@ By default second factor authentication enabled for each user, you can change it
|
|
70
83
|
|
71
84
|
this will disable two factor authentication for local users
|
72
85
|
|
73
|
-
|
86
|
+
This gem is compatible with Google Authenticator (https://support.google.com/accounts/answer/1066447?hl=en). You can generate provisioning uris by invoking the following method on your model:
|
74
87
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
88
|
+
user.provisioning_uri #This assumes a user model with an email attributes
|
89
|
+
|
90
|
+
This provisioning uri can then be turned in to a QR code if desired so that users may add the app to Google Authenticator easily. Once this is done they may retrieve a one-time password directly from the Google Authenticator app as well as through whatever method you define in `send_two_factor_authentication_code`
|
91
|
+
|
92
|
+
### Example
|
80
93
|
|
81
|
-
|
94
|
+
[TwoFactorAuthenticationExample](https://github.com/Houdini/TwoFactorAuthenticationExample)
|
@@ -7,8 +7,8 @@ class Devise::TwoFactorAuthenticationController < DeviseController
|
|
7
7
|
|
8
8
|
def update
|
9
9
|
render :show and return if params[:code].nil?
|
10
|
-
|
11
|
-
if
|
10
|
+
|
11
|
+
if resource.authenticate_otp(params[:code])
|
12
12
|
warden.session(resource_name)[:need_two_factor_authentication] = false
|
13
13
|
sign_in resource_name, resource, :bypass => true
|
14
14
|
redirect_to stored_location_for(resource_name) || :root
|
@@ -16,7 +16,7 @@ class Devise::TwoFactorAuthenticationController < DeviseController
|
|
16
16
|
else
|
17
17
|
resource.second_factor_attempts_count += 1
|
18
18
|
resource.save
|
19
|
-
set_flash_message :
|
19
|
+
set_flash_message :error, :attempt_failed
|
20
20
|
if resource.max_login_attempts?
|
21
21
|
sign_out(resource)
|
22
22
|
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<h2>Access
|
2
|
-
<p>Please contact your system administrator
|
1
|
+
<h2>Access completely denied as you have reached your attempts limit = <%= @limit %>.</h2>
|
2
|
+
<p>Please contact your system administrator.</p>
|
3
3
|
|
data/config/locales/en.yml
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
class TwoFactorAuthenticationAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
2
|
-
def
|
2
|
+
def up
|
3
3
|
change_table :<%= table_name %> do |t|
|
4
|
-
t.string :
|
4
|
+
t.string :otp_secret_key
|
5
5
|
t.integer :second_factor_attempts_count, :default => 0
|
6
6
|
end
|
7
|
+
|
8
|
+
add_index :<%= table_name %>, :otp_secret_key, :unique => true
|
9
|
+
end
|
10
|
+
|
11
|
+
def down
|
12
|
+
remove_column :<%= table_name %>, :otp_secret_key
|
13
|
+
remove_column :<%= table_name %>, :second_factor_attempts_count
|
7
14
|
end
|
8
15
|
end
|
@@ -1,15 +1,18 @@
|
|
1
1
|
require 'two_factor_authentication/version'
|
2
|
-
require 'randexp'
|
3
2
|
require 'devise'
|
4
|
-
require 'digest'
|
5
3
|
require 'active_support/concern'
|
4
|
+
require "active_model"
|
5
|
+
require "active_record"
|
6
|
+
require "active_support/core_ext/class/attribute_accessors"
|
7
|
+
require "cgi"
|
8
|
+
require "rotp"
|
6
9
|
|
7
10
|
module Devise
|
8
|
-
mattr_accessor :login_code_random_pattern
|
9
|
-
@@login_code_random_pattern = /\w+/
|
10
|
-
|
11
11
|
mattr_accessor :max_login_attempts
|
12
12
|
@@max_login_attempts = 3
|
13
|
+
|
14
|
+
mattr_accessor :allowed_otp_drift_seconds
|
15
|
+
@@allowed_otp_drift_seconds = 30
|
13
16
|
end
|
14
17
|
|
15
18
|
module TwoFactorAuthentication
|
@@ -10,17 +10,24 @@ module TwoFactorAuthentication
|
|
10
10
|
private
|
11
11
|
|
12
12
|
def handle_two_factor_authentication
|
13
|
-
|
13
|
+
unless devise_controller?
|
14
14
|
Devise.mappings.keys.flatten.any? do |scope|
|
15
15
|
if signed_in?(scope) and warden.session(scope)[:need_two_factor_authentication]
|
16
|
-
|
17
|
-
redirect_to two_factor_authentication_path_for(scope)
|
18
|
-
return
|
16
|
+
handle_failed_second_factor(scope)
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
22
|
+
def handle_failed_second_factor(scope)
|
23
|
+
if request.format.present? and request.format.html?
|
24
|
+
session["#{scope}_return_tor"] = request.path if request.get?
|
25
|
+
redirect_to two_factor_authentication_path_for(scope)
|
26
|
+
else
|
27
|
+
render nothing: true, status: :unauthorized
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
24
31
|
def two_factor_authentication_path_for(resource_or_scope = nil)
|
25
32
|
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
26
33
|
change_path = "#{scope}_two_factor_authentication_path"
|
@@ -1,10 +1,7 @@
|
|
1
1
|
Warden::Manager.after_authentication do |user, auth, options|
|
2
2
|
if user.respond_to?(:need_two_factor_authentication?)
|
3
3
|
if auth.session(options[:scope])[:need_two_factor_authentication] = user.need_two_factor_authentication?(auth.request)
|
4
|
-
|
5
|
-
user.second_factor_pass_code = Digest::MD5.hexdigest(code)
|
6
|
-
user.save
|
7
|
-
user.send_two_factor_authentication_code(code)
|
4
|
+
user.send_two_factor_authentication_code
|
8
5
|
end
|
9
6
|
end
|
10
7
|
end
|
@@ -5,23 +5,61 @@ module Devise
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
module ClassMethods
|
8
|
-
|
9
|
-
end
|
8
|
+
def has_one_time_password(options = {})
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
10
|
+
cattr_accessor :otp_column_name
|
11
|
+
self.otp_column_name = "otp_secret_key"
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
include InstanceMethodsOnActivation
|
14
|
+
|
15
|
+
before_create { self.otp_column = ROTP::Base32.random_base32 }
|
18
16
|
|
19
|
-
|
20
|
-
|
17
|
+
if respond_to?(:attributes_protected_by_default)
|
18
|
+
def self.attributes_protected_by_default #:nodoc:
|
19
|
+
super + [self.otp_column_name]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
::Devise::Models.config(self, :max_login_attempts, :allowed_otp_drift_seconds)
|
21
24
|
end
|
22
25
|
|
23
|
-
|
24
|
-
|
26
|
+
module InstanceMethodsOnActivation
|
27
|
+
def authenticate_otp(code, options = {})
|
28
|
+
totp = ROTP::TOTP.new(self.otp_column)
|
29
|
+
drift = options[:drift] || self.class.allowed_otp_drift_seconds
|
30
|
+
|
31
|
+
totp.verify_with_drift(code, drift)
|
32
|
+
end
|
33
|
+
|
34
|
+
def otp_code(time = Time.now)
|
35
|
+
ROTP::TOTP.new(self.otp_column).at(time)
|
36
|
+
end
|
37
|
+
|
38
|
+
def provisioning_uri(account = nil)
|
39
|
+
account ||= self.email if self.respond_to?(:email)
|
40
|
+
ROTP::TOTP.new(self.otp_column).provisioning_uri(account)
|
41
|
+
end
|
42
|
+
|
43
|
+
def otp_column
|
44
|
+
self.send(self.class.otp_column_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def otp_column=(attr)
|
48
|
+
self.send("#{self.class.otp_column_name}=", attr)
|
49
|
+
end
|
50
|
+
|
51
|
+
def need_two_factor_authentication?(request)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_two_factor_authentication_code
|
56
|
+
raise NotImplementedError.new("No default implementation - please define in your class.")
|
57
|
+
end
|
58
|
+
|
59
|
+
def max_login_attempts?
|
60
|
+
second_factor_attempts_count >= self.class.max_login_attempts
|
61
|
+
end
|
62
|
+
|
25
63
|
end
|
26
64
|
end
|
27
65
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
include AuthenticatedModelHelper
|
3
|
+
|
4
|
+
|
5
|
+
describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do
|
6
|
+
let(:instance) { AuthenticatedModelHelper.create_new_user }
|
7
|
+
subject { instance.otp_code(time) }
|
8
|
+
let(:time) { 1392852456 }
|
9
|
+
|
10
|
+
it "should return an error if no secret is set" do
|
11
|
+
expect {
|
12
|
+
subject
|
13
|
+
}.to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
context "secret is set" do
|
17
|
+
before :each do
|
18
|
+
instance.otp_secret_key = "2z6hxkdwi3uvrnpn"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should not return an error" do
|
22
|
+
subject
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with a known time" do
|
26
|
+
let(:time) { 1392852756 }
|
27
|
+
|
28
|
+
it "should return a known result" do
|
29
|
+
expect(subject).to eq(562202)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe Devise::Models::TwoFactorAuthenticatable, '#authenticate_otp' do
|
36
|
+
let(:instance) { AuthenticatedModelHelper.create_new_user }
|
37
|
+
|
38
|
+
before :each do
|
39
|
+
instance.otp_secret_key = "2z6hxkdwi3uvrnpn"
|
40
|
+
end
|
41
|
+
|
42
|
+
def do_invoke code, options = {}
|
43
|
+
instance.authenticate_otp(code, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be able to authenticate a recently created code" do
|
47
|
+
code = instance.otp_code
|
48
|
+
expect(do_invoke(code)).to eq(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should not authenticate an old code" do
|
52
|
+
code = instance.otp_code(1.minutes.ago.to_i)
|
53
|
+
expect(do_invoke(code)).to eq(false)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe Devise::Models::TwoFactorAuthenticatable, '#send_two_factor_authentication_code' do
|
58
|
+
|
59
|
+
it "should raise an error by default" do
|
60
|
+
instance = AuthenticatedModelHelper.create_new_user
|
61
|
+
expect {
|
62
|
+
instance.send_two_factor_authentication_code
|
63
|
+
}.to raise_error(NotImplementedError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be overrideable" do
|
67
|
+
instance = AuthenticatedModelHelper.create_new_user_with_overrides
|
68
|
+
expect(instance.send_two_factor_authentication_code).to eq("Code sent")
|
69
|
+
end
|
70
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
require 'two_factor_authentication'
|
5
|
+
|
6
|
+
|
7
|
+
Dir["#{Dir.pwd}/spec/support/**/*.rb"].each {|f| require f}
|
8
|
+
|
9
|
+
|
10
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
config.filter_run :focus
|
15
|
+
|
16
|
+
# Run specs in random order to surface order dependencies. If you find an
|
17
|
+
# order dependency and want to debug it, you can fix the order by providing
|
18
|
+
# the seed, which is printed after each run.
|
19
|
+
# --seed 1234
|
20
|
+
config.order = 'random'
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module AuthenticatedModelHelper
|
2
|
+
|
3
|
+
class User
|
4
|
+
extend ActiveModel::Callbacks
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include Devise::Models::TwoFactorAuthenticatable
|
7
|
+
|
8
|
+
define_model_callbacks :create
|
9
|
+
attr_accessor :otp_secret_key, :email
|
10
|
+
|
11
|
+
has_one_time_password
|
12
|
+
end
|
13
|
+
|
14
|
+
class UserWithOverrides < User
|
15
|
+
|
16
|
+
def send_two_factor_authentication_code
|
17
|
+
"Code sent"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_new_user
|
22
|
+
User.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_new_user_with_overrides
|
26
|
+
UserWithOverrides.new
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -24,9 +24,11 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
25
|
s.require_paths = ["lib"]
|
26
26
|
|
27
|
+
s.add_development_dependency "rspec"
|
27
28
|
s.add_runtime_dependency 'rails', '>= 3.1.1'
|
28
29
|
s.add_runtime_dependency 'devise'
|
29
30
|
s.add_runtime_dependency 'randexp'
|
31
|
+
s.add_runtime_dependency 'rotp'
|
30
32
|
|
31
33
|
s.add_development_dependency 'bundler'
|
32
34
|
end
|
metadata
CHANGED
@@ -1,83 +1,105 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: two_factor_authentication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0
|
5
|
-
prerelease:
|
4
|
+
version: '1.0'
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Dmitrii Golub
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-03-28 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
14
27
|
- !ruby/object:Gem::Dependency
|
15
28
|
name: rails
|
16
29
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
30
|
requirements:
|
19
|
-
- -
|
31
|
+
- - '>='
|
20
32
|
- !ruby/object:Gem::Version
|
21
33
|
version: 3.1.1
|
22
34
|
type: :runtime
|
23
35
|
prerelease: false
|
24
36
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
37
|
requirements:
|
27
|
-
- -
|
38
|
+
- - '>='
|
28
39
|
- !ruby/object:Gem::Version
|
29
40
|
version: 3.1.1
|
30
41
|
- !ruby/object:Gem::Dependency
|
31
42
|
name: devise
|
32
43
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
44
|
requirements:
|
35
|
-
- -
|
45
|
+
- - '>='
|
36
46
|
- !ruby/object:Gem::Version
|
37
47
|
version: '0'
|
38
48
|
type: :runtime
|
39
49
|
prerelease: false
|
40
50
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
51
|
requirements:
|
43
|
-
- -
|
52
|
+
- - '>='
|
44
53
|
- !ruby/object:Gem::Version
|
45
54
|
version: '0'
|
46
55
|
- !ruby/object:Gem::Dependency
|
47
56
|
name: randexp
|
48
57
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
58
|
requirements:
|
51
|
-
- -
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rotp
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
52
74
|
- !ruby/object:Gem::Version
|
53
75
|
version: '0'
|
54
76
|
type: :runtime
|
55
77
|
prerelease: false
|
56
78
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
79
|
requirements:
|
59
|
-
- -
|
80
|
+
- - '>='
|
60
81
|
- !ruby/object:Gem::Version
|
61
82
|
version: '0'
|
62
83
|
- !ruby/object:Gem::Dependency
|
63
84
|
name: bundler
|
64
85
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
86
|
requirements:
|
67
|
-
- -
|
87
|
+
- - '>='
|
68
88
|
- !ruby/object:Gem::Version
|
69
89
|
version: '0'
|
70
90
|
type: :development
|
71
91
|
prerelease: false
|
72
92
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
93
|
requirements:
|
75
|
-
- -
|
94
|
+
- - '>='
|
76
95
|
- !ruby/object:Gem::Version
|
77
96
|
version: '0'
|
78
|
-
description:
|
79
|
-
|
80
|
-
|
97
|
+
description: |2
|
98
|
+
### Features ###
|
99
|
+
* control sms code pattern
|
100
|
+
* configure max login attempts
|
101
|
+
* per user level control if he really need two factor authentication
|
102
|
+
* your own sms logic
|
81
103
|
email:
|
82
104
|
- dmitrii.golub@gmail.com
|
83
105
|
executables: []
|
@@ -105,30 +127,35 @@ files:
|
|
105
127
|
- lib/two_factor_authentication/routes.rb
|
106
128
|
- lib/two_factor_authentication/schema.rb
|
107
129
|
- lib/two_factor_authentication/version.rb
|
130
|
+
- spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
- spec/support/authenticated_model_helper.rb
|
108
133
|
- two_factor_authentication.gemspec
|
109
134
|
homepage: https://github.com/Houdini/two_factor_authentication
|
110
135
|
licenses: []
|
136
|
+
metadata: {}
|
111
137
|
post_install_message:
|
112
138
|
rdoc_options: []
|
113
139
|
require_paths:
|
114
140
|
- lib
|
115
141
|
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
-
none: false
|
117
142
|
requirements:
|
118
|
-
- -
|
143
|
+
- - '>='
|
119
144
|
- !ruby/object:Gem::Version
|
120
145
|
version: '0'
|
121
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
147
|
requirements:
|
124
|
-
- -
|
148
|
+
- - '>='
|
125
149
|
- !ruby/object:Gem::Version
|
126
150
|
version: '0'
|
127
151
|
requirements: []
|
128
152
|
rubyforge_project: two_factor_authentication
|
129
|
-
rubygems_version: 1.
|
153
|
+
rubygems_version: 2.1.11
|
130
154
|
signing_key:
|
131
|
-
specification_version:
|
155
|
+
specification_version: 4
|
132
156
|
summary: Two factor authentication plugin for devise
|
133
|
-
test_files:
|
157
|
+
test_files:
|
158
|
+
- spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb
|
159
|
+
- spec/spec_helper.rb
|
160
|
+
- spec/support/authenticated_model_helper.rb
|
134
161
|
has_rdoc:
|