devise_gauth 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +144 -0
- data/app/controllers/devise/checkga_controller.rb +57 -0
- data/app/controllers/devise/displayqr_controller.rb +82 -0
- data/app/views/devise/checkga/show.html.erb +7 -0
- data/app/views/devise/displayqr/show.html.erb +20 -0
- data/config/locales/en.yml +21 -0
- data/lib/devise_google_authenticatable/controllers/helpers.rb +72 -0
- data/lib/devise_google_authenticatable/hooks/totp_authenticatable.rb +17 -0
- data/lib/devise_google_authenticatable/models/google_authenticatable.rb +102 -0
- data/lib/devise_google_authenticatable/orm/active_record.rb +23 -0
- data/lib/devise_google_authenticatable/patches/display_qr.rb +43 -0
- data/lib/devise_google_authenticatable/patches.rb +13 -0
- data/lib/devise_google_authenticatable/rails.rb +21 -0
- data/lib/devise_google_authenticatable/routes.rb +18 -0
- data/lib/devise_google_authenticatable/schema.rb +39 -0
- data/lib/devise_google_authenticatable/version.rb +5 -0
- data/lib/devise_google_authenticatable/views/helpers.rb +52 -0
- data/lib/devise_google_authenticator.rb +44 -0
- data/lib/generators/active_record/devise_google_authenticator_generator.rb +15 -0
- data/lib/generators/active_record/templates/migration.rb +19 -0
- data/lib/generators/devise_google_authenticator/devise_google_authenticator_generator.rb +37 -0
- data/lib/generators/devise_google_authenticator/install_generator.rb +34 -0
- data/lib/generators/devise_google_authenticator/views_generator.rb +21 -0
- data/lib/generators/mongoid/devise_google_authenticator_generator.rb +28 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e9a3c30a2e628f5e3fe99a0c4dedaf30f0dcbeced3c6a52a199fa150dd393b4b
|
4
|
+
data.tar.gz: 3259e009ed15a53e1e154451ad95963e862726f10f3966d46a9f910dfab80fa8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc4bacfe402e8e98667326ec1c73cca6f2c05c545a7e5b450dc3c50d917ef90565aeb1942de381addd3782f83747d324f796148800f1770bc537f2b6af84ce82
|
7
|
+
data.tar.gz: aba6c26a7a3e7d667425a9aac361ae1a1dd7e60e76600f6b2ebc55f58fd904f98a654caa80528382d507305eae4cf54775bf210361d7e35654f3c719c0a5539d
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Pharmony SA
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# Devise Google Authenticator
|
2
|
+
|
3
|
+
This is a fork of the [devise](https://github.com/plataformatec/devise) extension to allow your app to utilise [Google Authenticator](http://code.google.com/p/google-authenticator/) for Time-based One Time Passwords (TOTP).
|
4
|
+
|
5
|
+
The current version of this gem support Rails 4.x, 5.x and 6.x.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add the gem to your Gemfile (don't forget devise too):
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'devise_gauth', '0.3.16'
|
13
|
+
```
|
14
|
+
|
15
|
+
Don't forget to "bundle install"
|
16
|
+
|
17
|
+
Before you can setup Devise Google Authenticator you need to [setup Devise first](https://github.com/heartcombo/devise#getting-started).
|
18
|
+
|
19
|
+
### Automatic Installation (Lets assume this is a bare bones app)
|
20
|
+
|
21
|
+
Run the following generator to add the necessary configuration options to Devise's config file:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
rails g devise_google_authenticator:install
|
25
|
+
```
|
26
|
+
|
27
|
+
After you've created your Devise user models (which is usually done with a "rails g devise MODEL"), set up your Google Authenticator additions:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
rails g devise_google_authenticator MODEL
|
31
|
+
```
|
32
|
+
|
33
|
+
Don't forget to migrate if you're NOT using Mongoid as your database ORM, Mongoid installations will have appropriate fields added to the model after the command above:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
rake db:migrate
|
37
|
+
```
|
38
|
+
|
39
|
+
### Installation With Existing Users
|
40
|
+
|
41
|
+
After the above steps have been performed, you'll need to generate secrets for each user:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
User.where(gauth_secret: nil).find_each do |user|
|
45
|
+
user.send(:assign_auth_secret)
|
46
|
+
user.save!
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
By default, users won't need to perform two-factor authentication (gauth_enabled='0'). By visiting /MODEL/displayqr (eg: /users/displayqr)
|
51
|
+
and submitting the form, two-factor authentication will then be turned on (gauth_enabled=1) and required for subsequent logins.
|
52
|
+
|
53
|
+
## Configuration Options
|
54
|
+
|
55
|
+
The install generator adds some options to the end of your Devise config file (config/initializers/devise.rb)
|
56
|
+
|
57
|
+
* `config.ga_timeout` - how long should the user be able to authenticate with their Google Authenticator token
|
58
|
+
* `config.ga_timedrift` - a multiplier which provides for drift between a user's clock (and therefore their OTP) and the system clock. This should be fine at 3.
|
59
|
+
* `config.ga_remembertime` - how long to remember the token for before requiring another. By default this is 1 month. To disable this setting change it to nil.
|
60
|
+
* `config.ga_appname` - If you want to set a custom application name instead of using the name of the Rails app.
|
61
|
+
* `config.ga_bypass_signup` - If you don't want to immediately forward newly registered or signed-up users to the Display QR page. If this is enabled, users will have to visit the /displayqr page to enable Google Authenticator.
|
62
|
+
|
63
|
+
## Custom Views
|
64
|
+
|
65
|
+
If you want to customise your views (which you likely will want to, as they're pretty ugly right now), you can use the generator:
|
66
|
+
|
67
|
+
```bash
|
68
|
+
rails g devise_google_authenticator:views
|
69
|
+
```
|
70
|
+
|
71
|
+
## Usage
|
72
|
+
|
73
|
+
With this extension enabled, the following is expected behaviour:
|
74
|
+
|
75
|
+
* When a user registers, they are forwarded onto the Display QR page (unless ga_bypass_signup is set to true). This allows them to add their new "token" to their mobile device, and enable, or disable, the functionality. To enable/disable the functionality, the user has to enter the current token.
|
76
|
+
* If users can't self-register, they're still able to visit this page by visiting /MODEL/displayqr (eg: /users/displayqr).
|
77
|
+
* If the function is enabled (for that user), when they sign in, they'll be prompted for their password (as per normal), but then redirected into the Check QR page. They have to enter their token (from their device) to then successfully authenticate.
|
78
|
+
* If configured (by default to 1 month), the user will only be prompted for the token every 1 month.
|
79
|
+
|
80
|
+
## I18n
|
81
|
+
|
82
|
+
The install generator also installs an english copy of a Devise Google Authenticator i18n file. This can be modified (or used to create other language versions) and is located at: config/locales/devise.google_authenticator.en.yml
|
83
|
+
|
84
|
+
## Testing
|
85
|
+
|
86
|
+
You can use [Docker](https://www.docker.com/) to build & run tests. To make it
|
87
|
+
even more easier this project uses [Earthly](https://earthly.dev/) so that you
|
88
|
+
can build the project's docker image with:
|
89
|
+
|
90
|
+
```
|
91
|
+
earthly +dev
|
92
|
+
```
|
93
|
+
|
94
|
+
Then you can run the tests using [Docker Compose](https://docs.docker.com/compose/):
|
95
|
+
|
96
|
+
```
|
97
|
+
docker compose run --rm gem
|
98
|
+
```
|
99
|
+
|
100
|
+
Or you can run the tests like on the CI using Earthly
|
101
|
+
(This reduces random failures between the CI and you environment):
|
102
|
+
|
103
|
+
```
|
104
|
+
earthly --allow-privileged +test
|
105
|
+
```
|
106
|
+
|
107
|
+
And in the case you'd like to test a different version of Ruby/Rails/Devise:
|
108
|
+
|
109
|
+
```
|
110
|
+
earthly --allow-privileged +test --EARTHLY_RUBY_VERSION=2.5 --EARTHLY_RAILS_VERSION=5.2.8.1 --EARTHLY_DEVISE_VERSION=4.8.1
|
111
|
+
```
|
112
|
+
|
113
|
+
## Thanks (and unknown contributors)
|
114
|
+
|
115
|
+
This extension would not exist without the following other projects and associated authors (Whom I have turned to for inspiration and definitely have helped contributing by providing awesome Devise extensions. A lot of this code has been refactored from various sources, in particular these - in particular Sergio and Devise_invitable for his excellent unit test code):
|
116
|
+
|
117
|
+
* Devise (José Valim, Carlos Antônio da Silva, Rodrigo Flores) https://github.com/plataformatec/devise
|
118
|
+
* Devise_invitable (Sergio Cambra) https://github.com/scambra/devise_invitable
|
119
|
+
* Devise_openid_authenticatable (Nat Budin) https://github.com/nbudin/devise_openid_authenticatable
|
120
|
+
* Devise_security_extension (Team Phatworx, Marco Scholl, Alexander Dreher) https://github.com/phatworx/devise_security_extension
|
121
|
+
* Ronald Arias https://github.com/ronald05arias
|
122
|
+
* Sunny Ng https://github.com/blahblahblah-
|
123
|
+
* Michael Guymon https://github.com/mguymon
|
124
|
+
* Mikkel Garcia https://github.com/mikkel
|
125
|
+
* Ricky Reusser https://github.com/rreusser
|
126
|
+
* Felipe Lima https://github.com/felipecsl
|
127
|
+
* Sylvain Utard https://github.com/redox
|
128
|
+
|
129
|
+
|
130
|
+
## Contributing to devise_gauth
|
131
|
+
|
132
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
133
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
134
|
+
* Fork the project
|
135
|
+
* Start a feature/bugfix branch
|
136
|
+
* Commit and push until you are happy with your contribution
|
137
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
138
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
139
|
+
|
140
|
+
## Copyright
|
141
|
+
|
142
|
+
Copyright (c) 2014 Christian Frichot. See LICENSE.txt for
|
143
|
+
further details.
|
144
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Devise::CheckgaController < Devise::SessionsController
|
4
|
+
if Rails.version >= '4'
|
5
|
+
prepend_before_action :devise_resource, only: [:show]
|
6
|
+
prepend_before_action :require_no_authentication, only: %i[show update]
|
7
|
+
else
|
8
|
+
prepend_before_filter :devise_resource, only: [:show]
|
9
|
+
prepend_before_filter :require_no_authentication, only: %i[show update]
|
10
|
+
end
|
11
|
+
|
12
|
+
include Devise::Controllers::Helpers
|
13
|
+
|
14
|
+
def show
|
15
|
+
@tmpid = params[:id]
|
16
|
+
if @tmpid.nil?
|
17
|
+
redirect_to :root
|
18
|
+
else
|
19
|
+
render :show
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def update
|
24
|
+
resource = resource_class.find_by_gauth_tmp(params[resource_name]['tmpid'])
|
25
|
+
|
26
|
+
if resource
|
27
|
+
|
28
|
+
if resource.validate_token(params[resource_name]['gauth_token'].to_i)
|
29
|
+
set_flash_message(:notice, :signed_in) if is_navigational_format?
|
30
|
+
sign_in(resource_name, resource)
|
31
|
+
warden.manager._run_callbacks(:after_set_user, resource, warden, { event: :authentication })
|
32
|
+
respond_with resource, location: after_sign_in_path_for(resource)
|
33
|
+
|
34
|
+
if resource.class.ga_remembertime
|
35
|
+
cookies.signed[:gauth] = {
|
36
|
+
value: resource.email << ',' << Time.now.to_i.to_s,
|
37
|
+
secure: !(Rails.env.test? || Rails.env.development?),
|
38
|
+
expires: (resource.class.ga_remembertime + 1.days).from_now
|
39
|
+
}
|
40
|
+
end
|
41
|
+
else
|
42
|
+
set_flash_message(:error, :error)
|
43
|
+
redirect_to :root
|
44
|
+
end
|
45
|
+
|
46
|
+
else
|
47
|
+
set_flash_message(:error, :error)
|
48
|
+
redirect_to :root
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def devise_resource
|
55
|
+
self.resource = resource_class.new
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devise/version'
|
4
|
+
|
5
|
+
class Devise::DisplayqrController < DeviseController
|
6
|
+
if Rails.version >= '4'
|
7
|
+
prepend_before_action :authenticate_scope!, only: %i[show update refresh]
|
8
|
+
else
|
9
|
+
prepend_before_filter :authenticate_scope!, only: %i[show update refresh]
|
10
|
+
end
|
11
|
+
|
12
|
+
include Devise::Controllers::Helpers
|
13
|
+
|
14
|
+
# GET /resource/displayqr
|
15
|
+
def show
|
16
|
+
if resource.nil? || resource.gauth_secret.nil?
|
17
|
+
sign_in resource_class.new, resource
|
18
|
+
redirect_to stored_location_for(scope) || :root
|
19
|
+
else
|
20
|
+
@tmpid = resource.assign_tmp
|
21
|
+
render :show
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update
|
26
|
+
if resource.gauth_tmp != params[resource_name]['tmpid'] || !resource.validate_token(params[resource_name]['gauth_token'].to_i)
|
27
|
+
set_flash_message(:error, :invalid_token)
|
28
|
+
render :show
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
if resource.set_gauth_enabled(params[resource_name]['gauth_enabled'])
|
33
|
+
set_flash_message :notice, (resource.gauth_enabled? ? :enabled : :disabled)
|
34
|
+
if Gem::Version.new(Devise::VERSION) < Gem::Version.new('4.2.0')
|
35
|
+
sign_in scope, resource, bypass: true
|
36
|
+
else
|
37
|
+
bypass_sign_in resource, scope: scope
|
38
|
+
end
|
39
|
+
|
40
|
+
respond_with resource, location: after_sign_in_path_for(resource)
|
41
|
+
else
|
42
|
+
render :show
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def refresh
|
47
|
+
unless resource.nil?
|
48
|
+
resource.send(:assign_auth_secret)
|
49
|
+
resource.save
|
50
|
+
set_flash_message :notice, :newtoken
|
51
|
+
sign_in scope, resource, bypass: true
|
52
|
+
redirect_to [resource_name, :displayqr]
|
53
|
+
else
|
54
|
+
redirect_to :root
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def scope
|
61
|
+
resource_name.to_sym
|
62
|
+
end
|
63
|
+
|
64
|
+
def authenticate_scope!
|
65
|
+
# https://github.com/AsteriskLabs/devise_google_authenticator/issues/29
|
66
|
+
send(:"authenticate_#{resource_name}!", force: true)
|
67
|
+
self.resource = send("current_#{resource_name}")
|
68
|
+
end
|
69
|
+
|
70
|
+
# 7/2/15 - Unsure if this is used anymore - @xntrik
|
71
|
+
def resource_params
|
72
|
+
if strong_parameters_enabled?
|
73
|
+
return params.require(resource_name.to_sym).permit(:gauth_enabled)
|
74
|
+
end
|
75
|
+
|
76
|
+
params
|
77
|
+
end
|
78
|
+
|
79
|
+
def strong_parameters_enabled?
|
80
|
+
defined?(ActionController::StrongParameters)
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<h2><%= I18n.t('submit_token_title', { scope: 'devise' }) %></h2>
|
2
|
+
|
3
|
+
<%= form_for(resource, as: resource_name, url: [resource_name, :checkga], html: { method: :put }) do |f| %>
|
4
|
+
<%= f.hidden_field :tmpid, { value: @tmpid } %>
|
5
|
+
<%= f.text_field :gauth_token, autocomplete: :off%>
|
6
|
+
<p><%= f.submit I18n.t('submit_token', { scope: 'devise' }) %></p>
|
7
|
+
<% end %>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<h2><%= I18n.t('title', { scope: 'devise.registration' }) %></h2>
|
2
|
+
|
3
|
+
<%= google_authenticator_qrcode(resource) %>
|
4
|
+
|
5
|
+
<%= form_for(resource, as: resource_name, url: [:refresh, resource_name, :displayqr], html: { method: :post }) do |f|%>
|
6
|
+
<p><%= f.submit I18n.t('newtoken', { scope: 'devise.registration' }) %></p>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<%= form_for(resource, as: resource_name, url: [resource_name, :displayqr], html: { method: :put }) do |f| %>
|
10
|
+
<%= devise_error_messages! %>
|
11
|
+
<h3><%= I18n.t('nice_request', { scope: 'devise.registration' }) %></h3>
|
12
|
+
<p><%= f.label :gauth_enabled, I18n.t('qrstatus', { scope: 'devise.registration' }) %><br />
|
13
|
+
<%= f.check_box :gauth_enabled %></p>
|
14
|
+
<%= f.hidden_field :tmpid, value: @tmpid %>
|
15
|
+
<p><%= f.label :gauth_token, I18n.t('enter_token', { scope: 'devise.registration' }) %><br />
|
16
|
+
<%= f.number_field :gauth_token, autocomplete: :off %>
|
17
|
+
|
18
|
+
<p><%= f.submit I18n.t('submit', { scope: 'devise.registration' }) %></p>
|
19
|
+
<% end %>
|
20
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
en:
|
2
|
+
devise:
|
3
|
+
submit_token: "Check Token"
|
4
|
+
submit_token_title: "Please enter your Google Authenticator token:"
|
5
|
+
registration:
|
6
|
+
title: "Your QR Code:"
|
7
|
+
nice_request: "Would you like to enable Google Authenticator?"
|
8
|
+
qrstatus: "Google Authenticator Status:"
|
9
|
+
enter_token: "Please enter your token number to continue"
|
10
|
+
submit: "Continue..."
|
11
|
+
newtoken: "Generate new token"
|
12
|
+
checkga:
|
13
|
+
user:
|
14
|
+
signed_in: "Signed in successfully from token."
|
15
|
+
error: "Sign in failed"
|
16
|
+
displayqr:
|
17
|
+
user:
|
18
|
+
status: "User status updated!"
|
19
|
+
newtoken: "You have updated to a new token - make sure you update your Google Authenticator application before continuing!!"
|
20
|
+
disabled: "2FA is currently disabled"
|
21
|
+
enabled: "2FA is currently enabled"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator
|
4
|
+
module Controllers
|
5
|
+
module Helpers
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
if Rails.version >= '4'
|
10
|
+
before_action :check_request_and_redirect_to_check_totp,
|
11
|
+
if: :user_signing_in?
|
12
|
+
else
|
13
|
+
before_filter :check_request_and_redirect_to_check_totp,
|
14
|
+
if: :user_signing_in?
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method :checkga_resource_path_name do |resource, id|
|
18
|
+
name = resource.class.name.singularize.underscore
|
19
|
+
name = name.split('/').last
|
20
|
+
"#{name}_checkga_path(id:'#{id}')"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def devise_sessions_controller?
|
27
|
+
instance_of?(Devise::SessionsController) ||
|
28
|
+
self.class.ancestors.include?(Devise::SessionsController)
|
29
|
+
end
|
30
|
+
|
31
|
+
def user_signing_in?
|
32
|
+
if devise_controller? && signed_in?(resource_name) &&
|
33
|
+
devise_sessions_controller? &&
|
34
|
+
action_name == 'create'
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_request_and_redirect_to_check_totp
|
42
|
+
# User successfully signed in AND has enabled 2FA
|
43
|
+
if signed_in?(resource_name) &&
|
44
|
+
warden.session(resource_name)[:with_totp_authentication]
|
45
|
+
|
46
|
+
resource = warden.authenticate!(auth_options)
|
47
|
+
|
48
|
+
# The user has 2FA and token has expired
|
49
|
+
if resource.respond_to?(:get_qr) && resource.require_token?(cookies.signed[:gauth])
|
50
|
+
|
51
|
+
tmpid = resource.assign_tmp # Assign a temporary key and fetch it
|
52
|
+
warden.logout # log the user out
|
53
|
+
|
54
|
+
# We head back into the checkga controller with the temporary id
|
55
|
+
# Because the model used for google auth may not always be the same,
|
56
|
+
# and may be a sub-model, the eval will evaluate the appropriate path
|
57
|
+
# name
|
58
|
+
# This change addresses https://github.com/AsteriskLabs/devise_google_authenticator/issues/7
|
59
|
+
respond_with resource,
|
60
|
+
location: eval(checkga_resource_path_name(resource,
|
61
|
+
tmpid))
|
62
|
+
# User is not using, OR not enabled for Google 2FA, OR is remembering
|
63
|
+
# token and therefore not asking for the moment
|
64
|
+
# carry on, nothing to see here.
|
65
|
+
else
|
66
|
+
respond_with resource, location: after_sign_in_path_for(resource)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Warden::Manager.after_authentication do |user, auth, _|
|
4
|
+
gauth_enabled = true
|
5
|
+
gauth_enabled = user.gauth_enabled? if user.respond_to?(:gauth_enabled?)
|
6
|
+
|
7
|
+
if gauth_enabled && user.respond_to?(:with_totp_authentication?)
|
8
|
+
with_totp_authentication = user.with_totp_authentication?
|
9
|
+
# Build Warden scope from user class name
|
10
|
+
user_scope = user.class.name.underscore.to_sym
|
11
|
+
# Ensure Warden knows about it
|
12
|
+
if auth.config[:default_strategies].keys.include?(user_scope)
|
13
|
+
scope = auth.session(user_scope)
|
14
|
+
scope[:with_totp_authentication] = with_totp_authentication
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rotp'
|
4
|
+
require 'devise_google_authenticatable/hooks/totp_authenticatable'
|
5
|
+
|
6
|
+
module Devise
|
7
|
+
module Models
|
8
|
+
module GoogleAuthenticatable
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
|
12
|
+
base.class_eval do
|
13
|
+
before_validation :assign_auth_secret, on: :create
|
14
|
+
include InstanceMethods
|
15
|
+
|
16
|
+
# Allow base model to override the gauth_enabled? method
|
17
|
+
unless base.method_defined?(:gauth_enabled?)
|
18
|
+
include GauthEnabledInstanceMethod
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
# Is the TOTP Authentication enabled
|
25
|
+
def with_totp_authentication?
|
26
|
+
gauth_enabled.to_i != 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_qr
|
30
|
+
gauth_secret
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_gauth_enabled(param)
|
34
|
+
update(gauth_enabled: param)
|
35
|
+
end
|
36
|
+
|
37
|
+
def assign_tmp
|
38
|
+
update(gauth_tmp: ROTP::Base32.random_base32(32),
|
39
|
+
gauth_tmp_datetime: Time.now)
|
40
|
+
gauth_tmp
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_token(token)
|
44
|
+
return false if gauth_tmp_datetime.nil?
|
45
|
+
|
46
|
+
return false if gauth_tmp_datetime < self.class.ga_timeout.ago
|
47
|
+
|
48
|
+
valid_vals = []
|
49
|
+
valid_vals << ROTP::TOTP.new(get_qr).at(Time.now)
|
50
|
+
(1..self.class.ga_timedrift).each do |cc|
|
51
|
+
valid_vals << ROTP::TOTP.new(get_qr).at(Time.now.ago(30 * cc))
|
52
|
+
valid_vals << ROTP::TOTP.new(get_qr).at(Time.now.in(30 * cc))
|
53
|
+
end
|
54
|
+
|
55
|
+
valid_vals.include?(token.to_i)
|
56
|
+
end
|
57
|
+
|
58
|
+
def require_token?(cookie)
|
59
|
+
return true if self.class.ga_remembertime.nil? || cookie.blank?
|
60
|
+
|
61
|
+
array = cookie.to_s.split ','
|
62
|
+
return true if array.count != 2
|
63
|
+
|
64
|
+
last_logged_in_email = array[0]
|
65
|
+
last_logged_in_time = array[1].to_i
|
66
|
+
|
67
|
+
last_logged_in_email != email ||
|
68
|
+
(Time.now.to_i - last_logged_in_time) > self.class.ga_remembertime
|
69
|
+
.to_i
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def assign_auth_secret
|
75
|
+
self.gauth_secret = ROTP::Base32.random_base32(64)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module GauthEnabledInstanceMethod
|
80
|
+
def gauth_enabled?
|
81
|
+
# Active_record seems to handle determining the status better this way
|
82
|
+
if gauth_enabled.respond_to?('to_i')
|
83
|
+
gauth_enabled.to_i != 0
|
84
|
+
# Mongoid does NOT have a .to_i for the Boolean return value, hence,
|
85
|
+
# we can just return it
|
86
|
+
else
|
87
|
+
gauth_enabled
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module ClassMethods
|
93
|
+
def find_by_gauth_tmp(gauth_tmp)
|
94
|
+
where(gauth_tmp: gauth_tmp).first
|
95
|
+
end
|
96
|
+
::Devise::Models.config(self, :ga_timeout, :ga_timedrift,
|
97
|
+
:ga_remembertime, :ga_appname,
|
98
|
+
:ga_bypass_signup)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator
|
4
|
+
module Orm
|
5
|
+
# This module contains handle schema (migrations):
|
6
|
+
#
|
7
|
+
# create_table :accounts do |t|
|
8
|
+
# t.gauth_secret
|
9
|
+
# t.gauth_enabled
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
|
13
|
+
module ActiveRecord
|
14
|
+
module Schema
|
15
|
+
include DeviseGoogleAuthenticator::Schema
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ActiveRecord::ConnectionAdapters::Table.send :include, DeviseGoogleAuthenticator::Orm::ActiveRecord::Schema
|
23
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, DeviseGoogleAuthenticator::Orm::ActiveRecord::Schema
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator::Patches
|
4
|
+
# patch Registrations controller to display the QR code
|
5
|
+
module DisplayQR
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
# arrr be the patch
|
10
|
+
alias_method :create_original, :create
|
11
|
+
|
12
|
+
define_method :create do
|
13
|
+
build_resource(sign_up_params)
|
14
|
+
|
15
|
+
if resource.save
|
16
|
+
yield resource if block_given?
|
17
|
+
if resource.active_for_authentication?
|
18
|
+
set_flash_message :notice, :signed_up if is_flashing_format?
|
19
|
+
sign_in(resource_name, resource)
|
20
|
+
|
21
|
+
if resource.respond_to? :gauth_enabled?
|
22
|
+
if resource.class.ga_bypass_signup
|
23
|
+
respond_with resource, location: after_sign_up_path_for(resource)
|
24
|
+
else
|
25
|
+
respond_with resource, location: { controller: 'displayqr', action: 'show' }
|
26
|
+
end
|
27
|
+
else
|
28
|
+
respond_with resource, location: after_sign_up_path_for(resource)
|
29
|
+
end
|
30
|
+
|
31
|
+
else
|
32
|
+
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
|
33
|
+
expire_data_after_sign_in!
|
34
|
+
respond_with resource, location: after_inactive_sign_up_path_for(resource)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
clean_up_passwords resource
|
38
|
+
respond_with resource
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator
|
4
|
+
module Patches
|
5
|
+
autoload :DisplayQR, 'devise_google_authenticatable/patches/display_qr'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def apply
|
9
|
+
Devise::RegistrationsController.send(:include, Patches::DisplayQR)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devise_google_authenticatable/controllers/helpers'
|
4
|
+
|
5
|
+
module DeviseGoogleAuthenticator
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
if Rails.version > '5'
|
8
|
+
ActiveSupport::Reloader.to_prepare do
|
9
|
+
DeviseGoogleAuthenticator::Patches.apply
|
10
|
+
end
|
11
|
+
else
|
12
|
+
ActionDispatch::Callbacks.to_prepare do
|
13
|
+
DeviseGoogleAuthenticator::Patches.apply
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveSupport.on_load(:action_controller) do
|
18
|
+
include DeviseGoogleAuthenticator::Controllers::Helpers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
module Routing
|
5
|
+
class Mapper
|
6
|
+
protected
|
7
|
+
|
8
|
+
# route for handle expired passwords
|
9
|
+
def devise_displayqr(mapping, controllers)
|
10
|
+
resource :displayqr, only: %i[show update], path: mapping.path_names[:displayqr], controller: controllers[:displayqr] do
|
11
|
+
post :refresh, path: mapping.path_names[:refresh], as: :refresh
|
12
|
+
end
|
13
|
+
|
14
|
+
resource :checkga, only: %i[show update], path: mapping.path_names[:checkga], controller: controllers[:checkga]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator
|
4
|
+
# add schema helper for migrations
|
5
|
+
module Schema
|
6
|
+
# Add gauth_secret columns in the resource's database tables
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# # For a new resource migration:
|
11
|
+
# create_table :the_resources do |t|
|
12
|
+
# t.gauth_secret
|
13
|
+
# t.gauth_enabled
|
14
|
+
# ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # or if the resource's table already exists, define a migration and put this in:
|
18
|
+
# change_table :the_resources do |t|
|
19
|
+
# t.string :gauth_secret
|
20
|
+
# t.boolean :gauth_enabled
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
def gauth_secret
|
24
|
+
apply_devise_schema :gauth_secret, String
|
25
|
+
end
|
26
|
+
|
27
|
+
def gauth_enabled
|
28
|
+
apply_devise_schema :gauth_enabled, Integer, { default: 0 }
|
29
|
+
end
|
30
|
+
|
31
|
+
def gauth_tmp
|
32
|
+
apply_devise_schema :gauth_tmp, String
|
33
|
+
end
|
34
|
+
|
35
|
+
def gauth_tmp_datetime
|
36
|
+
apply_devise_schema :gauth_tmp_datetime, Datetime
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rqrcode'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module DeviseGoogleAuthenticator
|
7
|
+
module Views
|
8
|
+
module Helpers
|
9
|
+
def build_qrcode_data_from(username, app, gauth_secret, qualifier = nil, issuer = nil)
|
10
|
+
data = "otpauth://totp/#{otpauth_user(username, app, qualifier)}"
|
11
|
+
data += "?secret=#{gauth_secret}"
|
12
|
+
data += "&issuer=#{issuer}" unless issuer.nil?
|
13
|
+
data
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_qrcode_from(data)
|
17
|
+
qrcode = RQRCode::QRCode.new(data, level: :m, mode: :byte_8bit)
|
18
|
+
png = qrcode.as_png(fill: 'white', color: 'black', border_modules: 1, module_px_size: 4)
|
19
|
+
"data:image/png;base64,#{Base64.encode64(png.to_s).strip}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def application_name_from(user)
|
23
|
+
user.class.ga_appname || if Rails.version < '6'
|
24
|
+
Rails.application.class.parent_name
|
25
|
+
else
|
26
|
+
Rails.application.class.module_parent_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def google_authenticator_qrcode(user, qualifier = nil, issuer = nil)
|
31
|
+
data = build_qrcode_data_from(
|
32
|
+
username_from_email(user.email),
|
33
|
+
application_name_from(user),
|
34
|
+
user.gauth_secret,
|
35
|
+
qualifier,
|
36
|
+
issuer
|
37
|
+
)
|
38
|
+
|
39
|
+
# data-uri is easier, so...
|
40
|
+
image_tag(build_qrcode_from(data), alt: 'Google Authenticator QRCode')
|
41
|
+
end
|
42
|
+
|
43
|
+
def otpauth_user(username, app, qualifier = nil)
|
44
|
+
"#{username}@#{app}#{qualifier}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def username_from_email(email)
|
48
|
+
/^(.*)@/.match(email)[1]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
4
|
+
require 'active_support/core_ext/integer'
|
5
|
+
require 'active_support/core_ext/string'
|
6
|
+
require 'active_support/ordered_hash'
|
7
|
+
require 'active_support/concern'
|
8
|
+
require 'devise'
|
9
|
+
|
10
|
+
module Devise # :nodoc:
|
11
|
+
mattr_accessor :ga_timeout
|
12
|
+
@@ga_timeout = 3.minutes
|
13
|
+
|
14
|
+
mattr_accessor :ga_timedrift
|
15
|
+
@@ga_timedrift = 3
|
16
|
+
|
17
|
+
mattr_accessor :ga_remembertime
|
18
|
+
@@ga_remembertime = 1.month
|
19
|
+
|
20
|
+
mattr_accessor :ga_appname
|
21
|
+
@@ga_appname = if Rails.version < '6'
|
22
|
+
Rails.application.class.parent_name
|
23
|
+
else
|
24
|
+
Rails.application.class.module_parent_name
|
25
|
+
end
|
26
|
+
|
27
|
+
mattr_accessor :ga_bypass_signup
|
28
|
+
@@ga_bypass_signup = false
|
29
|
+
end
|
30
|
+
|
31
|
+
# a security extension for devise
|
32
|
+
module DeviseGoogleAuthenticator
|
33
|
+
autoload :Schema, 'devise_google_authenticatable/schema'
|
34
|
+
autoload :Patches, 'devise_google_authenticatable/patches'
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'devise_google_authenticatable/routes'
|
38
|
+
require 'devise_google_authenticatable/rails'
|
39
|
+
require 'devise_google_authenticatable/orm/active_record'
|
40
|
+
require 'devise_google_authenticatable/controllers/helpers'
|
41
|
+
require 'devise_google_authenticatable/views/helpers'
|
42
|
+
ActionView::Base.send :include, DeviseGoogleAuthenticator::Views::Helpers
|
43
|
+
|
44
|
+
Devise.add_module :google_authenticatable, controller: :google_authenticatable, model: 'devise_google_authenticatable/models/google_authenticatable', route: :displayqr
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/active_record'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Generators
|
7
|
+
class DeviseGoogleAuthenticatorGenerator < ActiveRecord::Generators::Base
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
def copy_devise_migration
|
11
|
+
migration_template "migration.rb", "db/migrate/devise_google_authenticator_add_to_#{table_name}.rb"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DeviseGoogleAuthenticatorAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
4
|
+
def self.up
|
5
|
+
change_table :<%= table_name %> do |t|
|
6
|
+
t.string :gauth_secret
|
7
|
+
t.string :gauth_enabled, default: '0'
|
8
|
+
t.string :gauth_tmp
|
9
|
+
t.datetime :gauth_tmp_datetime
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.down
|
15
|
+
change_table :<%= table_name %> do |t|
|
16
|
+
t.remove :gauth_secret, :gauth_enabled, :gauth_tmp, :gauth_tmp_datetime
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator
|
4
|
+
module Generators
|
5
|
+
class DeviseGoogleAuthenticatorGenerator < Rails::Generators::NamedBase
|
6
|
+
namespace 'devise_google_authenticator'
|
7
|
+
|
8
|
+
desc 'Add :google_authenticatable directive in the given model, plus accessors. Also generate migration for ActiveRecord'
|
9
|
+
|
10
|
+
def inject_devise_google_authenticator_content
|
11
|
+
path = File.join('app', 'models', "#{file_path}.rb")
|
12
|
+
|
13
|
+
return unless File.exist?(path)
|
14
|
+
|
15
|
+
inject_into_file(path, 'google_authenticatable, :', after: 'devise :')
|
16
|
+
inject_into_file(path, 'gauth_enabled, :gauth_tmp, :gauth_tmp_datetime, :', after: 'attr_accessible :') if needs_attr_accessible?
|
17
|
+
inject_into_class(path, class_name, "\tattr_accessor :gauth_token\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
hook_for :orm
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def needs_attr_accessible?
|
25
|
+
rails_3? && !strong_parameters_enabled?
|
26
|
+
end
|
27
|
+
|
28
|
+
def rails_3?
|
29
|
+
Rails::VERSION::MAJOR == 3
|
30
|
+
end
|
31
|
+
|
32
|
+
def strong_parameters_enabled?
|
33
|
+
defined?(ActionController::StrongParameters)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeviseGoogleAuthenticator
|
4
|
+
module Generators
|
5
|
+
# Install Generator
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path('../../templates', __FILE__)
|
8
|
+
|
9
|
+
desc 'Install the devise google authenticator extension'
|
10
|
+
|
11
|
+
def add_configs
|
12
|
+
inject_into_file 'config/initializers/devise.rb', "\n # ==> Devise Google Authenticator Extension\n # Configure extension for devise\n\n" +
|
13
|
+
" # How long should the user have to enter their token. To change the default, uncomment and change the below:\n" +
|
14
|
+
" # config.ga_timeout = 3.minutes\n\n" +
|
15
|
+
" # Change time drift settings for valid token values. To change the default, uncomment and change the below:\n" +
|
16
|
+
" # config.ga_timedrift = 3\n\n" +
|
17
|
+
" # Change setting to how long to remember device before requiring another token. Change to nil to turn feature off.\n" +
|
18
|
+
" # To change the default, uncomment and change the below:\n" +
|
19
|
+
" # config.ga_remembertime = 1.month\n\n" +
|
20
|
+
" # Change setting to assign the application name used by code generator. Defaults to Rails.application.class.parent_name.\n" +
|
21
|
+
" # To change the default, uncomment and change the below:\n" +
|
22
|
+
" # config.ga_appname = 'example.com'\n\n" +
|
23
|
+
" # Change setting to bypass the Display QR page immediately after a user sign's up\n" +
|
24
|
+
" # To change the default, uncomment and change the below. Defaults to false:\n" +
|
25
|
+
" # config.ga_bypass_signup = true\n\n" +
|
26
|
+
"\n", before: /end[ |\n|]+\Z/
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_locale
|
30
|
+
copy_file '../../../config/locales/en.yml', 'config/locales/devise.google_authenticator.en.yml'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'generators/devise/views_generator'
|
4
|
+
|
5
|
+
module DeviseGoogleAuthenticator
|
6
|
+
module Generators
|
7
|
+
class ViewsGenerator < Rails::Generators::Base
|
8
|
+
desc 'Copies all Devise Google Authenticator views to your application.'
|
9
|
+
|
10
|
+
argument :scope, required: false, default: nil,
|
11
|
+
desc: 'The scope to copy views to'
|
12
|
+
|
13
|
+
include ::Devise::Generators::ViewPathTemplates
|
14
|
+
source_root File.expand_path('../../../../app/views/devise', __FILE__)
|
15
|
+
def copy_views
|
16
|
+
view_directory :checkga
|
17
|
+
view_directory :displayqr
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/named_base'
|
4
|
+
require 'generators/devise/orm_helpers'
|
5
|
+
|
6
|
+
module Mongoid
|
7
|
+
module Generators
|
8
|
+
class DeviseGoogleAuthenticatorGenerator < Rails::Generators::NamedBase
|
9
|
+
include Devise::Generators::OrmHelpers
|
10
|
+
|
11
|
+
def inject_field_types
|
12
|
+
inject_into_file model_path, migration_data, after: "include Mongoid::Document\n" if model_exists?
|
13
|
+
end
|
14
|
+
|
15
|
+
def migration_data
|
16
|
+
<<RUBY
|
17
|
+
# Google Authenticator
|
18
|
+
field :gauth_secret, type: String
|
19
|
+
field :gauth_enabled, type: Boolean, default: 'f'
|
20
|
+
field :gauth_tmp, type: String
|
21
|
+
field :gauth_tmp_datetime, type: DateTime
|
22
|
+
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devise_gauth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pharmony SA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionmailer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.8.1
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '6'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 5.2.8.1
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: devise
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 4.9.3
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 4.9.3
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: railties
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 5.2.8.1
|
54
|
+
- - "<"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '6'
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 5.2.8.1
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '6'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rotp
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '1.6'
|
74
|
+
type: :runtime
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '1.6'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rqrcode
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 2.1.2
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 2.1.2
|
95
|
+
description: Devise Google Authenticator Extension, for adding Google's OTP to your
|
96
|
+
Rails apps!
|
97
|
+
email:
|
98
|
+
- dev@pharmony.eu
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- LICENSE.txt
|
104
|
+
- README.md
|
105
|
+
- app/controllers/devise/checkga_controller.rb
|
106
|
+
- app/controllers/devise/displayqr_controller.rb
|
107
|
+
- app/views/devise/checkga/show.html.erb
|
108
|
+
- app/views/devise/displayqr/show.html.erb
|
109
|
+
- config/locales/en.yml
|
110
|
+
- lib/devise_google_authenticatable/controllers/helpers.rb
|
111
|
+
- lib/devise_google_authenticatable/hooks/totp_authenticatable.rb
|
112
|
+
- lib/devise_google_authenticatable/models/google_authenticatable.rb
|
113
|
+
- lib/devise_google_authenticatable/orm/active_record.rb
|
114
|
+
- lib/devise_google_authenticatable/patches.rb
|
115
|
+
- lib/devise_google_authenticatable/patches/display_qr.rb
|
116
|
+
- lib/devise_google_authenticatable/rails.rb
|
117
|
+
- lib/devise_google_authenticatable/routes.rb
|
118
|
+
- lib/devise_google_authenticatable/schema.rb
|
119
|
+
- lib/devise_google_authenticatable/version.rb
|
120
|
+
- lib/devise_google_authenticatable/views/helpers.rb
|
121
|
+
- lib/devise_google_authenticator.rb
|
122
|
+
- lib/generators/active_record/devise_google_authenticator_generator.rb
|
123
|
+
- lib/generators/active_record/templates/migration.rb
|
124
|
+
- lib/generators/devise_google_authenticator/devise_google_authenticator_generator.rb
|
125
|
+
- lib/generators/devise_google_authenticator/install_generator.rb
|
126
|
+
- lib/generators/devise_google_authenticator/views_generator.rb
|
127
|
+
- lib/generators/mongoid/devise_google_authenticator_generator.rb
|
128
|
+
homepage: http://github.com/pharmony/devise_gauth
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata:
|
132
|
+
allowed_push_host: https://rubygems.org
|
133
|
+
homepage_uri: http://github.com/pharmony/devise_gauth
|
134
|
+
source_code_uri: http://github.com/pharmony/devise_gauth
|
135
|
+
changelog_uri: http://github.com/pharmony/devise_gauth/blob/master/CHANGELOG.md
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '2.7'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubygems_version: 3.1.6
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Devise Google Authenticator Extension
|
155
|
+
test_files: []
|