two_factor_authentication 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -0
- data/README.md +81 -0
- data/Rakefile +1 -0
- data/app/controllers/devise/two_factor_authentication_controller.rb +43 -0
- data/app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb +3 -0
- data/app/views/devise/two_factor_authentication/show.html.erb +10 -0
- data/config/locales/en.yml +4 -0
- data/lib/generators/active_record/templates/migration.rb +8 -0
- data/lib/generators/active_record/two_factor_authentication_generator.rb +14 -0
- data/lib/generators/two_factor_authentication/two_factor_authentication_generator.rb +17 -0
- data/lib/two_factor_authentication.rb +27 -0
- data/lib/two_factor_authentication/controllers/helpers.rb +32 -0
- data/lib/two_factor_authentication/hooks/two_factor_authenticatable.rb +10 -0
- data/lib/two_factor_authentication/models/two_factor_authenticatable.rb +28 -0
- data/lib/two_factor_authentication/orm/active_record.rb +12 -0
- data/lib/two_factor_authentication/rails.rb +7 -0
- data/lib/two_factor_authentication/routes.rb +9 -0
- data/lib/two_factor_authentication/schema.rb +11 -0
- data/lib/two_factor_authentication/version.rb +3 -0
- data/two_factor_authentication.gemspec +32 -0
- metadata +133 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (C) 2012 Dmitrii Golub
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
## Two factor authentication for Devise
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
* control sms code pattern
|
6
|
+
* configure max login attempts
|
7
|
+
* per user level control if he really need two factor authentication
|
8
|
+
* your own sms logic
|
9
|
+
|
10
|
+
## Configuration
|
11
|
+
|
12
|
+
### Initial Setup
|
13
|
+
|
14
|
+
In a Rails environment, require the gem in your Gemfile:
|
15
|
+
|
16
|
+
gem 'two_factor_authentication', git: "http://github.com/Houdini/two_factor_authentication.git"
|
17
|
+
|
18
|
+
Once that's done, run:
|
19
|
+
|
20
|
+
bundle install
|
21
|
+
|
22
|
+
|
23
|
+
### Automatic installation
|
24
|
+
|
25
|
+
In order to add two factor authorisation to a model, run the command:
|
26
|
+
|
27
|
+
bundle exec rails g two_factor_authentication MODEL
|
28
|
+
|
29
|
+
Where MODEL is your model name (e.g. User or Admin). This generator will add `:two_factor_authenticatable` to your model
|
30
|
+
and create a migration in `db/migrate/`, which will add `::second_factor_pass_code` and `:second_factor_attempts_count` to your table.
|
31
|
+
Finally, run the migration with:
|
32
|
+
|
33
|
+
bundle exec rake db:migrate
|
34
|
+
|
35
|
+
|
36
|
+
### Manual installation
|
37
|
+
|
38
|
+
To manually enable two factor authentication for the User model, you should add two_factor_authentication to your devise line, like:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
devise :database_authenticatable, :registerable,
|
42
|
+
:recoverable, :rememberable, :trackable, :validatable, :two_factor_authenticatable
|
43
|
+
```
|
44
|
+
|
45
|
+
Two default parameters
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
config.login_code_random_pattern = /\w+/
|
49
|
+
config.max_login_attempts = 3
|
50
|
+
```
|
51
|
+
|
52
|
+
Possible random patterns
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
/\d{5}/
|
56
|
+
/\w{4,8}/
|
57
|
+
```
|
58
|
+
|
59
|
+
see more https://github.com/benburkert/randexp
|
60
|
+
|
61
|
+
### Customisation
|
62
|
+
|
63
|
+
By default second factor authentication enabled for each user, you can change it with this method in your User model:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
def need_two_factor_authentication?(request)
|
67
|
+
request.ip != '127.0.0.1'
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
this will disable two factor authentication for local users
|
72
|
+
|
73
|
+
Your send sms logic should be in this method in your User model:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
def send_two_factor_authentication_code(code)
|
77
|
+
puts code
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
This example just puts the code in the logs.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Devise::TwoFactorAuthenticationController < DeviseController
|
2
|
+
prepend_before_filter :authenticate_scope!
|
3
|
+
before_filter :prepare_and_validate, :handle_two_factor_authentication
|
4
|
+
|
5
|
+
def show
|
6
|
+
end
|
7
|
+
|
8
|
+
def update
|
9
|
+
render :show and return if params[:code].nil?
|
10
|
+
md5 = Digest::MD5.hexdigest(params[:code])
|
11
|
+
if md5.eql?(resource.second_factor_pass_code)
|
12
|
+
warden.session(resource_name)[:need_two_factor_authentication] = false
|
13
|
+
sign_in resource_name, resource, :bypass => true
|
14
|
+
redirect_to stored_location_for(resource_name) || :root
|
15
|
+
resource.update_attribute(:second_factor_attempts_count, 0)
|
16
|
+
else
|
17
|
+
resource.second_factor_attempts_count += 1
|
18
|
+
resource.save
|
19
|
+
set_flash_message :notice, :attempt_failed
|
20
|
+
if resource.max_login_attempts?
|
21
|
+
sign_out(resource)
|
22
|
+
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
|
23
|
+
else
|
24
|
+
render :show
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def authenticate_scope!
|
32
|
+
self.resource = send("current_#{resource_name}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare_and_validate
|
36
|
+
redirect_to :root and return if resource.nil?
|
37
|
+
@limit = resource.class.max_login_attempts
|
38
|
+
if resource.max_login_attempts?
|
39
|
+
sign_out(resource)
|
40
|
+
render :template => 'devise/two_factor_authentication/max_login_attempts_reached' and return
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<h2>Enter your personal code</h2>
|
2
|
+
|
3
|
+
<p><%= flash[:notice] %></p>
|
4
|
+
|
5
|
+
<%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
|
6
|
+
<%= text_field_tag :code %>
|
7
|
+
<%= submit_tag "Submit" %>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class TwoFactorAuthenticationAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
change_table :<%= table_name %> do |t|
|
4
|
+
t.string :second_factor_pass_code , :limit => 32
|
5
|
+
t.integer :second_factor_attempts_count, :default => 0
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Generators
|
5
|
+
class TwoFactorAuthenticationGenerator < ActiveRecord::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def copy_two_factor_authentication_migration
|
9
|
+
migration_template "migration.rb", "db/migrate/two_factor_authentication_add_to_#{table_name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module TwoFactorAuthenticatable
|
2
|
+
module Generators
|
3
|
+
class TwoFactorAuthenticationGenerator < Rails::Generators::NamedBase
|
4
|
+
namespace "two_factor_authentication"
|
5
|
+
|
6
|
+
desc "Adds :two_factor_authenticable directive in the given model. It also generates an active record migration."
|
7
|
+
|
8
|
+
def inject_two_factor_authentication_content
|
9
|
+
path = File.join("app", "models", "#{file_path}.rb")
|
10
|
+
inject_into_file(path, "two_factor_authenticatable, :", :after => "devise :") if File.exists?(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
hook_for :orm
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'two_factor_authentication/version'
|
2
|
+
require 'randexp'
|
3
|
+
require 'devise'
|
4
|
+
require 'digest'
|
5
|
+
require 'active_support/concern'
|
6
|
+
|
7
|
+
module Devise
|
8
|
+
mattr_accessor :login_code_random_pattern
|
9
|
+
@@login_code_random_pattern = /\w+/
|
10
|
+
|
11
|
+
mattr_accessor :max_login_attempts
|
12
|
+
@@max_login_attempts = 3
|
13
|
+
end
|
14
|
+
|
15
|
+
module TwoFactorAuthentication
|
16
|
+
autoload :Schema, 'two_factor_authentication/schema'
|
17
|
+
module Controllers
|
18
|
+
autoload :Helpers, 'two_factor_authentication/controllers/helpers'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Devise.add_module :two_factor_authenticatable, :model => 'two_factor_authentication/models/two_factor_authenticatable', :controller => :two_factor_authentication, :route => :two_factor_authentication
|
23
|
+
|
24
|
+
require 'two_factor_authentication/orm/active_record'
|
25
|
+
require 'two_factor_authentication/routes'
|
26
|
+
require 'two_factor_authentication/models/two_factor_authenticatable'
|
27
|
+
require 'two_factor_authentication/rails'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module TwoFactorAuthentication
|
2
|
+
module Controllers
|
3
|
+
module Helpers
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
before_filter :handle_two_factor_authentication
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def handle_two_factor_authentication
|
13
|
+
if not request.format.nil? and request.format.html? and not devise_controller?
|
14
|
+
Devise.mappings.keys.flatten.any? do |scope|
|
15
|
+
if signed_in?(scope) and warden.session(scope)[:need_two_factor_authentication]
|
16
|
+
session["#{scope}_return_tor"] = request.path if request.get?
|
17
|
+
redirect_to two_factor_authentication_path_for(scope)
|
18
|
+
return
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def two_factor_authentication_path_for(resource_or_scope = nil)
|
25
|
+
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
26
|
+
change_path = "#{scope}_two_factor_authentication_path"
|
27
|
+
send(change_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Warden::Manager.after_authentication do |user, auth, options|
|
2
|
+
if user.respond_to?(:need_two_factor_authentication?)
|
3
|
+
if auth.session(options[:scope])[:need_two_factor_authentication] = user.need_two_factor_authentication?(auth.request)
|
4
|
+
code = user.generate_two_factor_code
|
5
|
+
user.second_factor_pass_code = Digest::MD5.hexdigest(code)
|
6
|
+
user.save
|
7
|
+
user.send_two_factor_authentication_code(code)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'two_factor_authentication/hooks/two_factor_authenticatable'
|
2
|
+
module Devise
|
3
|
+
module Models
|
4
|
+
module TwoFactorAuthenticatable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
::Devise::Models.config(self, :login_code_random_pattern, :max_login_attempts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def need_two_factor_authentication?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_two_factor_code
|
16
|
+
self.class.login_code_random_pattern.gen
|
17
|
+
end
|
18
|
+
|
19
|
+
def send_two_factor_authentication_code(code)
|
20
|
+
p "Code is #{code}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def max_login_attempts?
|
24
|
+
second_factor_attempts_count >= self.class.max_login_attempts
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module TwoFactorAuthentication
|
2
|
+
module Orm
|
3
|
+
module ActiveRecord
|
4
|
+
module Schema
|
5
|
+
include TwoFactorAuthentication::Schema
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveRecord::ConnectionAdapters::Table.send :include, TwoFactorAuthentication::Orm::ActiveRecord::Schema
|
12
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, TwoFactorAuthentication::Orm::ActiveRecord::Schema
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
class Mapper
|
3
|
+
protected
|
4
|
+
|
5
|
+
def devise_two_factor_authentication(mapping, controllers)
|
6
|
+
resource :two_factor_authentication, :only => [:show, :update], :path => mapping.path_names[:two_factor_authentication], :controller => controllers[:two_factor_authentication]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module TwoFactorAuthentication
|
2
|
+
module Schema
|
3
|
+
def second_factor_pass_code
|
4
|
+
apply_devise_schema :second_factor_pass_code, String, :limit => 32
|
5
|
+
end
|
6
|
+
|
7
|
+
def second_factor_attempts_count
|
8
|
+
apply_devise_schema :second_factor_attempts_count, Integer, :default => 0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "two_factor_authentication/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "two_factor_authentication"
|
7
|
+
s.version = TwoFactorAuthentication::VERSION
|
8
|
+
s.authors = ["Dmitrii Golub"]
|
9
|
+
s.email = ["dmitrii.golub@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/Houdini/two_factor_authentication"
|
11
|
+
s.summary = %q{Two factor authentication plugin for devise}
|
12
|
+
s.description = <<-EOF
|
13
|
+
### Features ###
|
14
|
+
* control sms code pattern
|
15
|
+
* configure max login attempts
|
16
|
+
* per user level control if he really need two factor authentication
|
17
|
+
* your own sms logic
|
18
|
+
EOF
|
19
|
+
|
20
|
+
s.rubyforge_project = "two_factor_authentication"
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
|
27
|
+
s.add_runtime_dependency 'rails', '>= 3.1.1'
|
28
|
+
s.add_runtime_dependency 'devise'
|
29
|
+
s.add_runtime_dependency 'randexp'
|
30
|
+
|
31
|
+
s.add_development_dependency 'bundler'
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: two_factor_authentication
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dmitrii Golub
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.1.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.1.1
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: devise
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: randexp
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: ! " ### Features ###\n * control sms code pattern\n * configure
|
79
|
+
max login attempts\n * per user level control if he really need two factor authentication\n
|
80
|
+
\ * your own sms logic\n"
|
81
|
+
email:
|
82
|
+
- dmitrii.golub@gmail.com
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- app/controllers/devise/two_factor_authentication_controller.rb
|
93
|
+
- app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb
|
94
|
+
- app/views/devise/two_factor_authentication/show.html.erb
|
95
|
+
- config/locales/en.yml
|
96
|
+
- lib/generators/active_record/templates/migration.rb
|
97
|
+
- lib/generators/active_record/two_factor_authentication_generator.rb
|
98
|
+
- lib/generators/two_factor_authentication/two_factor_authentication_generator.rb
|
99
|
+
- lib/two_factor_authentication.rb
|
100
|
+
- lib/two_factor_authentication/controllers/helpers.rb
|
101
|
+
- lib/two_factor_authentication/hooks/two_factor_authenticatable.rb
|
102
|
+
- lib/two_factor_authentication/models/two_factor_authenticatable.rb
|
103
|
+
- lib/two_factor_authentication/orm/active_record.rb
|
104
|
+
- lib/two_factor_authentication/rails.rb
|
105
|
+
- lib/two_factor_authentication/routes.rb
|
106
|
+
- lib/two_factor_authentication/schema.rb
|
107
|
+
- lib/two_factor_authentication/version.rb
|
108
|
+
- two_factor_authentication.gemspec
|
109
|
+
homepage: https://github.com/Houdini/two_factor_authentication
|
110
|
+
licenses: []
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project: two_factor_authentication
|
129
|
+
rubygems_version: 1.8.24
|
130
|
+
signing_key:
|
131
|
+
specification_version: 3
|
132
|
+
summary: Two factor authentication plugin for devise
|
133
|
+
test_files: []
|