active_model_otp 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/CHANGELOG.md +9 -0
- data/README.md +21 -13
- data/lib/active_model/one_time_password.rb +10 -3
- data/lib/active_model/otp/version.rb +1 -1
- data/lib/active_model_otp.rb +1 -1
- data/test/one_time_password_test.rb +8 -2
- metadata +6 -17
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjdjOTMxYzkxNjI1MGM0ODdiZmNkZmFiYTZiMjBlZjc4ZTQzMjg2Mw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTQ4OGJlNTQzOTViZWEzNDU1MWU1YmQ3Y2Q5Njc3ZDQ0ZjdmMDVkYQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YWIyZDViZGE0YWRhYjc2M2YyZjRkYWVhMDI3NjA4YjgxMzRhY2E1MjNkOWM4
|
10
|
+
MmE5YjZlOTlhYmVkMDhkZjNjZjMzOTYzNzkzNTQ0ODZjMGEzOTRjZTc1OGNj
|
11
|
+
NDYwNDRiNmEzNmNjZWJhYTQxNzdhNzE2MzBjYzVhNjFhMzAzMWQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjdkNDlkYmRjNWM1MTRlZDQzZTMwNzFkZGE4OGMwMzU5NmQ1YWExMDY4YTA2
|
14
|
+
NTMwOTgwYWE5NjdkM2M1MzdiMDc3MGZhMWM0YWUzMDA2NDFlNDFmNWM3ZTlk
|
15
|
+
Y2ViODliODQ4YzQxYmY1Y2UyNWNiYjIzY2QwMDQzZDllNWFhYjQ=
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#v1.0.0
|
2
|
+
- Avoid overriding predefined otp_column value when initializing resource (Ilan Stern) https://github.com/heapsource/active_model_otp/pull/10
|
3
|
+
- Pad OTP codes with less than 6 digits (Johan Brissmyr) https://github.com/heapsource/active_model_otp/pull/7
|
4
|
+
- Get rid of deprecation warnings in Rails 4.1 (Nick DeMonner)
|
5
|
+
|
6
|
+
#v0.1.0
|
7
|
+
- OTP codes can be in 5 or 6 digits (André Luis Leal Cardoso Junior)
|
8
|
+
- Require 'cgi', rotp needs it for encoding parameters (André Luis Leal Cardoso Junior)
|
9
|
+
- Change column name for otp secret key (robertomiranda)
|
data/README.md
CHANGED
@@ -37,17 +37,22 @@ class User < ActiveRecord::Base
|
|
37
37
|
end
|
38
38
|
```
|
39
39
|
|
40
|
+
Note: If you're adding this to an existing user model you'll need to generate *otp_secret_key* with a migration like:
|
41
|
+
```ruby
|
42
|
+
User.all.each { |user| user.update_attribute(:otp_secret_key, ROTP::Base32.random_base32) }
|
43
|
+
```
|
44
|
+
|
40
45
|
|
41
46
|
##Usage
|
42
47
|
|
43
48
|
The has_one_time_password sentence provides to the model some useful methods in order to implement our TFA system. AMo:Otp generates one time passwords according to [RFC 4226](http://tools.ietf.org/html/rfc4226) and the [HOTP RFC](http://tools.ietf.org/html/draft-mraihi-totp-timebased-00). This is compatible with Google Authenticator apps available for Android and iPhone, and now in use on GMail.
|
44
49
|
|
45
|
-
The otp_secret_key is saved automatically when a object is created,
|
50
|
+
The otp_secret_key is saved automatically when a object is created,
|
46
51
|
|
47
52
|
```ruby
|
48
53
|
user = User.create(email: "hello@heapsource.com")
|
49
54
|
user.otp_secret_key
|
50
|
-
=> "jt3gdd2qm6su5iqh"
|
55
|
+
=> "jt3gdd2qm6su5iqh"
|
51
56
|
```
|
52
57
|
|
53
58
|
**Note:** You can fork the applications for [iPhone](https://github.com/heapsource/google-authenticator) & [Android](https://github.com/heapsource/google-authenticator.android) and customize it
|
@@ -57,6 +62,12 @@ user.otp_secret_key
|
|
57
62
|
user.otp_code # => '186522'
|
58
63
|
sleep 30
|
59
64
|
user.otp_code # => '850738'
|
65
|
+
|
66
|
+
# Override current time
|
67
|
+
user.otp_code(time: Time.now + 3600) # => '317438'
|
68
|
+
|
69
|
+
# Don't zero-pad to six digits
|
70
|
+
user.otp_code(padding: false) # => '438'
|
60
71
|
```
|
61
72
|
|
62
73
|
### Authenticating using a code
|
@@ -80,11 +91,11 @@ user.authenticate_otp('186522', drift: 60) # => true
|
|
80
91
|
The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate provisioning URI's to use with the QR Code scanner built into the app.
|
81
92
|
|
82
93
|
```ruby
|
83
|
-
# Use you user's emails for generate the
|
84
|
-
user.
|
94
|
+
# Use you user's emails for generate the provisioning_url
|
95
|
+
user.provisioning_uri # => 'otpauth://totp/hello@heapsource.com?secret=2z6hxkdwi3uvrnpn'
|
85
96
|
|
86
|
-
# Use a custom fied for generate the
|
87
|
-
user.
|
97
|
+
# Use a custom fied for generate the provisioning_url
|
98
|
+
user.provisioning_uri("hello") # => 'otpauth://totp/hello?secret=2z6hxkdwi3uvrnpn'
|
88
99
|
```
|
89
100
|
|
90
101
|
This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
|
@@ -119,13 +130,10 @@ puts "Current code #{user.otp_code}"
|
|
119
130
|
|
120
131
|
### Useful Examples
|
121
132
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
#### Sendind code via email with Twilio
|
127
|
-
|
128
|
-
#### Using with Mongoid
|
133
|
+
- [Generate QR code with rqrcode gem](https://github.com/heapsource/active_model_otp/wiki/Generate-QR-code-with-rqrcode-gem)
|
134
|
+
- Generating QR Code with Google Charts API
|
135
|
+
- [Sendind code via email with Twilio](https://github.com/heapsource/active_model_otp/wiki/Send-code-via-Twilio-SMS)
|
136
|
+
- Using with Mongoid
|
129
137
|
|
130
138
|
## Contributing
|
131
139
|
|
@@ -10,7 +10,7 @@ module ActiveModel
|
|
10
10
|
|
11
11
|
include InstanceMethodsOnActivation
|
12
12
|
|
13
|
-
before_create { self.otp_column
|
13
|
+
before_create { self.otp_column ||= ROTP::Base32.random_base32 }
|
14
14
|
|
15
15
|
if respond_to?(:attributes_protected_by_default)
|
16
16
|
def self.attributes_protected_by_default #:nodoc:
|
@@ -30,8 +30,15 @@ module ActiveModel
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def otp_code(
|
34
|
-
|
33
|
+
def otp_code(options = {})
|
34
|
+
if options.is_a? Hash
|
35
|
+
time = options.fetch(:time, Time.now)
|
36
|
+
padding = options.fetch(:padding, true)
|
37
|
+
else
|
38
|
+
time = options
|
39
|
+
padding = true
|
40
|
+
end
|
41
|
+
ROTP::TOTP.new(self.otp_column).at(time, padding)
|
35
42
|
end
|
36
43
|
|
37
44
|
def provisioning_uri(account = nil)
|
data/lib/active_model_otp.rb
CHANGED
@@ -27,8 +27,14 @@ class OtpTest < MiniTest::Unit::TestCase
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_otp_code
|
30
|
-
assert_match(
|
31
|
-
assert_match(
|
30
|
+
assert_match(/^\d{6}$/, @user.otp_code.to_s)
|
31
|
+
assert_match(/^\d{6}$/, @visitor.otp_code.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_otp_code_padding
|
35
|
+
@user.otp_column = 'kw5jhligwqaiw7jc'
|
36
|
+
assert_match(/^\d{6}$/, @user.otp_code(time: 2160, padding: true).to_s)
|
37
|
+
assert_match(/^\d{3}$/, @user.otp_code(time: 2160, padding: false).to_s)
|
32
38
|
end
|
33
39
|
|
34
40
|
def test_provisioning_uri_with_provided_account
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_model_otp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Guillermo Iguaran
|
@@ -11,12 +10,11 @@ authors:
|
|
11
10
|
autorequire:
|
12
11
|
bindir: bin
|
13
12
|
cert_chain: []
|
14
|
-
date:
|
13
|
+
date: 2014-08-01 00:00:00.000000000 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: activemodel
|
18
17
|
requirement: !ruby/object:Gem::Requirement
|
19
|
-
none: false
|
20
18
|
requirements:
|
21
19
|
- - ! '>='
|
22
20
|
- !ruby/object:Gem::Version
|
@@ -24,7 +22,6 @@ dependencies:
|
|
24
22
|
type: :runtime
|
25
23
|
prerelease: false
|
26
24
|
version_requirements: !ruby/object:Gem::Requirement
|
27
|
-
none: false
|
28
25
|
requirements:
|
29
26
|
- - ! '>='
|
30
27
|
- !ruby/object:Gem::Version
|
@@ -32,7 +29,6 @@ dependencies:
|
|
32
29
|
- !ruby/object:Gem::Dependency
|
33
30
|
name: rotp
|
34
31
|
requirement: !ruby/object:Gem::Requirement
|
35
|
-
none: false
|
36
32
|
requirements:
|
37
33
|
- - ! '>='
|
38
34
|
- !ruby/object:Gem::Version
|
@@ -40,7 +36,6 @@ dependencies:
|
|
40
36
|
type: :runtime
|
41
37
|
prerelease: false
|
42
38
|
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
none: false
|
44
39
|
requirements:
|
45
40
|
- - ! '>='
|
46
41
|
- !ruby/object:Gem::Version
|
@@ -48,7 +43,6 @@ dependencies:
|
|
48
43
|
- !ruby/object:Gem::Dependency
|
49
44
|
name: bundler
|
50
45
|
requirement: !ruby/object:Gem::Requirement
|
51
|
-
none: false
|
52
46
|
requirements:
|
53
47
|
- - ~>
|
54
48
|
- !ruby/object:Gem::Version
|
@@ -56,7 +50,6 @@ dependencies:
|
|
56
50
|
type: :development
|
57
51
|
prerelease: false
|
58
52
|
version_requirements: !ruby/object:Gem::Requirement
|
59
|
-
none: false
|
60
53
|
requirements:
|
61
54
|
- - ~>
|
62
55
|
- !ruby/object:Gem::Version
|
@@ -64,7 +57,6 @@ dependencies:
|
|
64
57
|
- !ruby/object:Gem::Dependency
|
65
58
|
name: rake
|
66
59
|
requirement: !ruby/object:Gem::Requirement
|
67
|
-
none: false
|
68
60
|
requirements:
|
69
61
|
- - ! '>='
|
70
62
|
- !ruby/object:Gem::Version
|
@@ -72,7 +64,6 @@ dependencies:
|
|
72
64
|
type: :development
|
73
65
|
prerelease: false
|
74
66
|
version_requirements: !ruby/object:Gem::Requirement
|
75
|
-
none: false
|
76
67
|
requirements:
|
77
68
|
- - ! '>='
|
78
69
|
- !ruby/object:Gem::Version
|
@@ -80,7 +71,6 @@ dependencies:
|
|
80
71
|
- !ruby/object:Gem::Dependency
|
81
72
|
name: minitest
|
82
73
|
requirement: !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
74
|
requirements:
|
85
75
|
- - ! '>='
|
86
76
|
- !ruby/object:Gem::Version
|
@@ -88,7 +78,6 @@ dependencies:
|
|
88
78
|
type: :development
|
89
79
|
prerelease: false
|
90
80
|
version_requirements: !ruby/object:Gem::Requirement
|
91
|
-
none: false
|
92
81
|
requirements:
|
93
82
|
- - ! '>='
|
94
83
|
- !ruby/object:Gem::Version
|
@@ -105,6 +94,7 @@ extra_rdoc_files: []
|
|
105
94
|
files:
|
106
95
|
- .gitignore
|
107
96
|
- .travis.yml
|
97
|
+
- CHANGELOG.md
|
108
98
|
- Gemfile
|
109
99
|
- LICENSE.txt
|
110
100
|
- README.md
|
@@ -120,27 +110,26 @@ files:
|
|
120
110
|
homepage: ''
|
121
111
|
licenses:
|
122
112
|
- MIT
|
113
|
+
metadata: {}
|
123
114
|
post_install_message:
|
124
115
|
rdoc_options: []
|
125
116
|
require_paths:
|
126
117
|
- lib
|
127
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
-
none: false
|
129
119
|
requirements:
|
130
120
|
- - ! '>='
|
131
121
|
- !ruby/object:Gem::Version
|
132
122
|
version: '0'
|
133
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
-
none: false
|
135
124
|
requirements:
|
136
125
|
- - ! '>='
|
137
126
|
- !ruby/object:Gem::Version
|
138
127
|
version: '0'
|
139
128
|
requirements: []
|
140
129
|
rubyforge_project:
|
141
|
-
rubygems_version:
|
130
|
+
rubygems_version: 2.2.2
|
142
131
|
signing_key:
|
143
|
-
specification_version:
|
132
|
+
specification_version: 4
|
144
133
|
summary: Adds methods to set and authenticate against one time passwords.
|
145
134
|
test_files:
|
146
135
|
- test/models/user.rb
|