one_time_password 0.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/MIT-LICENSE +20 -0
- data/README.md +88 -0
- data/Rakefile +3 -0
- data/lib/generators/one_time_password/install_generator.rb +59 -0
- data/lib/generators/one_time_password/templates/app/models/one_time_authentication.rb +5 -0
- data/lib/generators/one_time_password/templates/config/initializers/one_time_password.rb +55 -0
- data/lib/generators/one_time_password/templates/db/migrate/create_one_time_authentication.rb.erb +18 -0
- data/lib/one_time_password/one_time_authentication_model.rb +147 -0
- data/lib/one_time_password/railtie.rb +4 -0
- data/lib/one_time_password/version.rb +3 -0
- data/lib/one_time_password.rb +3 -0
- data/lib/tasks/one_time_password_tasks.rake +4 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 341d54de2197c31d888d896395ae79c514d3d7899a27637c66d8ec9899e8cf97
|
4
|
+
data.tar.gz: 1f6346bd6e38f32a52394c0ccb37e3058fcd956ef3878ef69f214bcc67ac617c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c76a03d196b10156ca63236f4fe835f3b63223bcadf63233e4b68bb2c7aebb196e4cb34ab99235a4418dbe7418c90aa3e31a3760e23df766c54971b9b24dc2b
|
7
|
+
data.tar.gz: 5750577c5bc3fb5c3bd13b1d39302da51e60867bf154168e69bc360c5907468d6c81fa2ac55faaceb4d4eea00c36f2f7a90dd3c99ffdca1c9de1a11efe999d2e
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# OneTimePassword
|
2
|
+
|
3
|
+
This Gem can be used to create 2FA (Two-Factor Authentication) function, email address verification function for member registration and etc in Ruby on Rails.
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem "one_time_password"
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install one_time_password
|
22
|
+
```
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
### Run command for an installation.
|
29
|
+
|
30
|
+
```bash
|
31
|
+
bundle exec rails g one_time_password:install
|
32
|
+
```
|
33
|
+
|
34
|
+
The following events will take place when using the install generator:
|
35
|
+
- An initializer file will be created at `config/initializers/one_time_password.rb`
|
36
|
+
- A migration file will be created at `db/migrate/xxxxxxxxxxxxxx_create_one_time_authentication.rb`
|
37
|
+
- A model file will be created at `app/models/one_time_authentication.rb`
|
38
|
+
|
39
|
+
And run migration.
|
40
|
+
|
41
|
+
```bash
|
42
|
+
bundle exec rails db:migrate
|
43
|
+
```
|
44
|
+
|
45
|
+
### Rewrite `FUNCTION_NAMES` and `CONTEXTS` in initializer settings.
|
46
|
+
|
47
|
+
Configuration in `config/initializers/one_time_password.rb`.
|
48
|
+
|
49
|
+
`FUNCTION_NAMES`: Using function_name in OneTimeAuthentication Model enum.
|
50
|
+
|
51
|
+
|
52
|
+
Hash, one of `CONTEXTS`:
|
53
|
+
| | |
|
54
|
+
| --- | --- |
|
55
|
+
| function_name (Symbol) | Name each function. |
|
56
|
+
| version (Integer) | Version each function_name. |
|
57
|
+
| expires_in (ActiveSupport::Duration) | Password validity time. |
|
58
|
+
| max_authenticate_password_count (Integer) | Number of times user can enter password each generated password. |
|
59
|
+
| password_length (Integer) | Password length. At 6, for example, the password would be 123456. |
|
60
|
+
| password_failed_limit (Integer)<br>password_failed_period (ActiveSupport::Duration) | If you try to authenticate with the wrong password a password_failed_limit times within the time set by password_failed_period, you will not be able to generate a new password. |
|
61
|
+
| | |
|
62
|
+
|
63
|
+
### See example and its sequence diagram
|
64
|
+
[here](#example-and-its-sequence-diagram)
|
65
|
+
|
66
|
+
### `OneTimePassword::OneTimeAuthentication`'s methods.
|
67
|
+
|
68
|
+
For more information, see the [implementation of OneTimePassword :: OneTimeAuthenticationModel](https://github.com/yosipy/one_time_password/blob/main/lib/one_time_password/one_time_authentication_model.rb).
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
## Example and its sequence diagram
|
73
|
+
|
74
|
+
See [sign up exsample](https://github.com/yosipy/one_time_password/blob/main/spec/dummy/app/controllers/test_users_controller.rb).
|
75
|
+
|
76
|
+
Sequence diagram.
|
77
|
+
|
78
|
+

|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
<!-- ## Contributing
|
83
|
+
Contribution directions go here. -->
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
## License
|
88
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "rails/generators/active_record"
|
3
|
+
|
4
|
+
module OneTimePassword
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
include ::Rails::Generators::Migration
|
7
|
+
|
8
|
+
class_option :warning_over_write, type: :boolean, default: false,
|
9
|
+
desc: "Orver write generator files."
|
10
|
+
|
11
|
+
def self.next_migration_number(dirname)
|
12
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
13
|
+
end
|
14
|
+
|
15
|
+
source_root File.expand_path('templates', __dir__)
|
16
|
+
|
17
|
+
def create_initializer_file
|
18
|
+
template = 'one_time_password'
|
19
|
+
file_path = "config/initializers/#{template}.rb"
|
20
|
+
|
21
|
+
if !File.exist?(file_path) || options[:warning_over_write]
|
22
|
+
template(file_path, File.expand_path(file_path))
|
23
|
+
else
|
24
|
+
::Kernel.warn "Initializers already exists: #{template}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_migration_file
|
29
|
+
template = 'create_one_time_authentication'
|
30
|
+
file_dir = 'db/migrate'
|
31
|
+
|
32
|
+
if !self.class.migration_exists?(File.expand_path(file_dir), template) || options[:warning_over_write]
|
33
|
+
migration_template(
|
34
|
+
"#{file_dir}/#{template}.rb.erb",
|
35
|
+
"#{File.expand_path(file_dir)}/#{template}.rb",
|
36
|
+
migration_version: migration_version
|
37
|
+
)
|
38
|
+
else
|
39
|
+
::Kernel.warn "Migration already exists: #{template}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_model_file
|
44
|
+
template = 'one_time_authentication'
|
45
|
+
file_path = "app/models/#{template}.rb"
|
46
|
+
if !File.exist?(file_path) || options[:warning_over_write]
|
47
|
+
template(file_path, File.expand_path(file_path))
|
48
|
+
else
|
49
|
+
::Kernel.warn "Model already exists: #{template}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def migration_version
|
56
|
+
format("[%d.%d]", ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module OneTimePassword
|
2
|
+
# Example: has sign_up, sign_in and change_email.
|
3
|
+
# Please rewrite!
|
4
|
+
|
5
|
+
# Using function_name in OneTimeAuthentication Model enum.
|
6
|
+
# ```
|
7
|
+
# # app/models/one_time_authentication.rb
|
8
|
+
# class OneTimeAuthentication < ActiveRecord::Base
|
9
|
+
# enum function_name: OneTimePassword::FUNCTION_NAMES
|
10
|
+
|
11
|
+
# include OneTimePassword::OneTimeAuthenticationModel
|
12
|
+
# end
|
13
|
+
# ```
|
14
|
+
FUNCTION_NAMES = {
|
15
|
+
sign_up: 0, sign_in: 1, change_email: 2
|
16
|
+
}
|
17
|
+
|
18
|
+
# {
|
19
|
+
# function_name (Symbol): Name each function.
|
20
|
+
# version (Integer): Version each function_name.
|
21
|
+
# expires_in (ActiveSupport::Duration): Password validity time.
|
22
|
+
# max_authenticate_password_count (Integer): Number of times user can enter password each generated password.
|
23
|
+
# password_length (Integer): Password length. At 6, for example, the password would be 123456.
|
24
|
+
# password_failed_limit (Integer) & password_failed_period (ActiveSupport::Duration):
|
25
|
+
# If you try to authenticate with the wrong password a password_failed_limit times
|
26
|
+
# within the time set by password_failed_period, you will not be able to generate a new password.
|
27
|
+
# }
|
28
|
+
CONTEXTS = [
|
29
|
+
{
|
30
|
+
function_name: :sign_up,
|
31
|
+
version: 0,
|
32
|
+
expires_in: 30.minutes,
|
33
|
+
max_authenticate_password_count: 5,
|
34
|
+
password_length: 6,
|
35
|
+
password_failed_limit: 10,
|
36
|
+
password_failed_period: 1.hour
|
37
|
+
},
|
38
|
+
{
|
39
|
+
function_name: :sign_in,
|
40
|
+
version: 0,
|
41
|
+
expires_in: 30.minutes,
|
42
|
+
max_authenticate_password_count: 5,
|
43
|
+
password_length: 10,
|
44
|
+
password_failed_limit: 10,
|
45
|
+
password_failed_period: 1.hour
|
46
|
+
},
|
47
|
+
# {
|
48
|
+
# function_name: :change_email,
|
49
|
+
# version: 0,
|
50
|
+
# expires_in: 30.minutes,
|
51
|
+
# max_authenticate_password_count: 5,
|
52
|
+
# password_length: 6
|
53
|
+
# },
|
54
|
+
]
|
55
|
+
end
|
data/lib/generators/one_time_password/templates/db/migrate/create_one_time_authentication.rb.erb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateOneTimeAuthentication < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
create_table :one_time_authentications do |t|
|
4
|
+
t.integer :function_name, null: false
|
5
|
+
t.integer :version, null: false
|
6
|
+
t.string :user_key, null: false, index: true
|
7
|
+
t.string :client_token
|
8
|
+
t.integer :password_length, null: false
|
9
|
+
t.string :password_digest, null: false
|
10
|
+
t.integer :expires_seconds, null: false
|
11
|
+
t.integer :failed_count, null: false, default: 0
|
12
|
+
t.integer :max_authenticate_password_count, null: false, default: 3
|
13
|
+
t.datetime :authenticated_at
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module OneTimePassword
|
2
|
+
module OneTimeAuthenticationModel
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_create :set_client_token
|
7
|
+
|
8
|
+
has_secure_password
|
9
|
+
|
10
|
+
scope :unauthenticated, -> {
|
11
|
+
where(authenticated_at: nil)
|
12
|
+
}
|
13
|
+
|
14
|
+
scope :recent, -> (time_ago) {
|
15
|
+
where(created_at: Time.zone.now.ago(time_ago)...)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def find_context(function_name, version: 0)
|
21
|
+
context = OneTimePassword::CONTEXTS
|
22
|
+
.select{ |context|
|
23
|
+
context[:function_name] == function_name &&
|
24
|
+
context[:version] == version
|
25
|
+
}
|
26
|
+
.first
|
27
|
+
|
28
|
+
if context.nil?
|
29
|
+
raise ArgumentError.new('Not found context.')
|
30
|
+
elsif context[:expires_in].class != ActiveSupport::Duration
|
31
|
+
raise RuntimeError.new('Mistake OneTimePassword::CONTEXTS[:expires_in]')
|
32
|
+
elsif context[:max_authenticate_password_count].class != Integer
|
33
|
+
raise RuntimeError.new('Mistake OneTimePassword::CONTEXTS[:max_authenticate_password_count]')
|
34
|
+
elsif context[:password_length].class != Integer
|
35
|
+
raise RuntimeError.new('Mistake OneTimePassword::CONTEXTS[:password_length]')
|
36
|
+
elsif context[:password_failed_limit].class != Integer
|
37
|
+
raise RuntimeError.new('Mistake OneTimePassword::CONTEXTS[:password_failed_limit]')
|
38
|
+
elsif context[:password_failed_period].class != ActiveSupport::Duration
|
39
|
+
raise RuntimeError.new('Mistake OneTimePassword::CONTEXTS[:password_failed_period]')
|
40
|
+
end
|
41
|
+
|
42
|
+
context
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_one_time_authentication(context, user_key, user_key_downcase: true)
|
46
|
+
user_key = user_key.downcase if user_key_downcase
|
47
|
+
|
48
|
+
recent_failed_authenticate_password_count =
|
49
|
+
OneTimeAuthentication
|
50
|
+
.recent_failed_authenticate_password_count(
|
51
|
+
user_key,
|
52
|
+
context[:password_failed_period]
|
53
|
+
)
|
54
|
+
|
55
|
+
if recent_failed_authenticate_password_count <= context[:password_failed_limit]
|
56
|
+
one_time_authentication = OneTimeAuthentication.new(
|
57
|
+
function_name: context[:function_name],
|
58
|
+
version: context[:version],
|
59
|
+
user_key: user_key,
|
60
|
+
password_length: context[:password_length],
|
61
|
+
expires_seconds: context[:expires_in].to_i,
|
62
|
+
max_authenticate_password_count: context[:max_authenticate_password_count],
|
63
|
+
)
|
64
|
+
one_time_authentication.set_password_and_password_length(context[:password_length])
|
65
|
+
one_time_authentication.save!
|
66
|
+
else
|
67
|
+
one_time_authentication = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
one_time_authentication
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_one_time_authentication(context, user_key, user_key_downcase: true)
|
74
|
+
user_key = user_key.downcase if user_key_downcase
|
75
|
+
|
76
|
+
OneTimeAuthentication
|
77
|
+
.where(function_name: context[:function_name])
|
78
|
+
.where(version: context[:version])
|
79
|
+
.where(user_key: user_key)
|
80
|
+
.last
|
81
|
+
end
|
82
|
+
|
83
|
+
def generate_random_password(length=6)
|
84
|
+
length.times.map{ SecureRandom.random_number(10) }.join
|
85
|
+
end
|
86
|
+
|
87
|
+
def recent_failed_authenticate_password_count(user_key, time_ago)
|
88
|
+
OneTimeAuthentication
|
89
|
+
.where(user_key: user_key)
|
90
|
+
.recent(time_ago)
|
91
|
+
.sum(:failed_count)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def expired?
|
96
|
+
!(self.created_at.to_f <= Time.zone.now.to_f &&
|
97
|
+
Time.zone.now.to_f <= self.created_at.to_f + self.expires_seconds.to_f)
|
98
|
+
end
|
99
|
+
|
100
|
+
def under_valid_failed_count?
|
101
|
+
self.failed_count < self.max_authenticate_password_count
|
102
|
+
end
|
103
|
+
|
104
|
+
def authenticate_one_time_client_token!(client_token)
|
105
|
+
if (self.client_token.present? &&
|
106
|
+
self.client_token == client_token)
|
107
|
+
# Refresh client_token, and return this token
|
108
|
+
new_client_token = self.set_client_token
|
109
|
+
self.save!
|
110
|
+
new_client_token
|
111
|
+
else
|
112
|
+
# Put invalid token(nil) in client_token, and return nil
|
113
|
+
self.client_token = nil
|
114
|
+
self.save!
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def authenticate_one_time_password!(password)
|
120
|
+
result =
|
121
|
+
if !self.expired? && self.under_valid_failed_count?
|
122
|
+
!!self.authenticate(password)
|
123
|
+
else
|
124
|
+
false
|
125
|
+
end
|
126
|
+
|
127
|
+
if result
|
128
|
+
self.authenticated_at = Time.zone.now
|
129
|
+
# Put invalid token(nil) in client_token, and return nil
|
130
|
+
self.client_token = nil
|
131
|
+
else
|
132
|
+
self.failed_count += 1
|
133
|
+
end
|
134
|
+
self.save!
|
135
|
+
|
136
|
+
result
|
137
|
+
end
|
138
|
+
|
139
|
+
def set_client_token
|
140
|
+
self.client_token = SecureRandom.urlsafe_base64
|
141
|
+
end
|
142
|
+
|
143
|
+
def set_password_and_password_length(length=6)
|
144
|
+
self.password = self.password_confirmation = OneTimeAuthentication.generate_random_password(length)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: one_time_password
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- yosipy
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bcrypt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.1.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.1.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: factory_bot_rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 6.2.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 6.2.0
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- yosi.contact@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- MIT-LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- lib/generators/one_time_password/install_generator.rb
|
94
|
+
- lib/generators/one_time_password/templates/app/models/one_time_authentication.rb
|
95
|
+
- lib/generators/one_time_password/templates/config/initializers/one_time_password.rb
|
96
|
+
- lib/generators/one_time_password/templates/db/migrate/create_one_time_authentication.rb.erb
|
97
|
+
- lib/one_time_password.rb
|
98
|
+
- lib/one_time_password/one_time_authentication_model.rb
|
99
|
+
- lib/one_time_password/railtie.rb
|
100
|
+
- lib/one_time_password/version.rb
|
101
|
+
- lib/tasks/one_time_password_tasks.rake
|
102
|
+
homepage: https://github.com/yosipy/one_time_password
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata:
|
106
|
+
homepage_uri: https://github.com/yosipy/one_time_password
|
107
|
+
source_code_uri: https://github.com/yosipy/one_time_password
|
108
|
+
changelog_uri: https://github.com/yosipy/one_time_password/blob/main/CHANGELOG.md
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubygems_version: 3.2.32
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: This Gem can be used to create 2FA (Two-Factor Authentication) function,
|
128
|
+
email address verification function for member registration and etc in Ruby on Rails.
|
129
|
+
test_files: []
|