action_auth 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +53 -0
- data/app/assets/stylesheets/action_auth/application.css +4 -0
- data/app/controllers/action_auth/sms_auths/requests_controller.rb +31 -0
- data/app/controllers/action_auth/sms_auths/sign_ins_controller.rb +27 -0
- data/app/models/action_auth/user.rb +9 -1
- data/app/views/action_auth/registrations/new.html.erb +3 -0
- data/app/views/action_auth/sessions/new.html.erb +4 -0
- data/app/views/action_auth/sms_auths/requests/new.html.erb +26 -0
- data/app/views/action_auth/sms_auths/sign_ins/show.html.erb +14 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20241020172209_add_phone_number_to_users.rb +7 -0
- data/lib/action_auth/configuration.rb +8 -0
- data/lib/action_auth/version.rb +1 -1
- data/lib/tasks/action_auth_tasks.rake +7 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2fa128174c9685bef3e348f40b7bfd48c28e26c23ba7c51d3e7088ce039d939a
|
4
|
+
data.tar.gz: d92884ccd4e77112736f5f6fd9753def05508d1eb0135cb103af6b7c9a392c62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23bbbe3ed9ae95fadef0eb10c1890d9ec865fff976b3c4837551b0803fcd722b49ecf5bd90ef9778b7f2851b2987ebcee8fdac1b80eb91125fdaaec0e87734a4
|
7
|
+
data.tar.gz: 777fabf39c4cc37dda6d487dd2514c46c8ff1a12a63890914e3fe2680d1c23a7542bcb3c9947aeb44c37a93f3206c1cd45a812335194c9b445c8348e81ddaebb
|
data/README.md
CHANGED
@@ -121,11 +121,18 @@ ActionAuth.configure do |config|
|
|
121
121
|
config.magic_link_enabled = true
|
122
122
|
config.passkey_only = true # Allows sign in with only a passkey
|
123
123
|
config.pwned_enabled = true # defined?(Pwned)
|
124
|
+
config.sms_auth_enabled = false
|
124
125
|
config.verify_email_on_sign_in = true
|
125
126
|
config.webauthn_enabled = true # defined?(WebAuthn)
|
126
127
|
config.webauthn_origin = "http://localhost:3000" # or "https://example.com"
|
127
128
|
config.webauthn_rp_name = Rails.application.class.to_s.deconstantize
|
128
129
|
end
|
130
|
+
|
131
|
+
Rails.application.config.after_initialize do
|
132
|
+
ActionAuth.configure do |config|
|
133
|
+
config.sms_send_class = SmsSender
|
134
|
+
end
|
135
|
+
end
|
129
136
|
```
|
130
137
|
|
131
138
|
## Features
|
@@ -152,6 +159,8 @@ These are the planned features for ActionAuth. The ones that are checked off are
|
|
152
159
|
|
153
160
|
⏳ - OAuth with Google, Facebook, Github, Twitter, etc.
|
154
161
|
|
162
|
+
✅ - SMS Authentication
|
163
|
+
|
155
164
|
✅ - Have I Been Pwned Integration
|
156
165
|
|
157
166
|
✅ - Account Deletion
|
@@ -245,6 +254,50 @@ an email to the user with a link that will log them in. This is a great way to a
|
|
245
254
|
without having to remember a password. This is especially useful for users who may not have a password
|
246
255
|
manager or have a hard time remembering passwords.
|
247
256
|
|
257
|
+
### SMS Authentication
|
258
|
+
|
259
|
+
SMS Authentication is disabled by default. The purpose of this is to allow users to authenticate
|
260
|
+
with a phone number. This is useful and specific to applications that may require a phone number
|
261
|
+
instead of an email address for authentication. The basic workflow for this is to register a phone
|
262
|
+
number, and then send a code to the phone number. The user will then enter the code to authenticate.
|
263
|
+
|
264
|
+
No password or email is required for this. I do not recommend enabling this feature for most applications.
|
265
|
+
|
266
|
+
You must set up your own SMS Provider. This is not included in the gem. You will need to configure the
|
267
|
+
`sms_send_class` to send the SMS code. This will expect a class method called `send_code` that will take in the parameters
|
268
|
+
`phone_number` and `code`.
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
require 'twilio-ruby'
|
272
|
+
|
273
|
+
class SmsSender
|
274
|
+
def self.send_code(phone_number, code)
|
275
|
+
account_sid = ENV['TWILIO_ACCOUNT_SID']
|
276
|
+
auth_token = ENV['TWILIO_AUTH_TOKEN']
|
277
|
+
from_number = ENV['TWILIO_PHONE_NUMBER']
|
278
|
+
|
279
|
+
client = Twilio::REST::Client.new(account_sid, auth_token)
|
280
|
+
|
281
|
+
client.messages.create(
|
282
|
+
from: from_number,
|
283
|
+
to: phone_number,
|
284
|
+
body: "Your verification code is #{code}"
|
285
|
+
)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
290
|
+
Since this file could live in the `app/models` or elsewhere, we will need to set its configuration after the Rails
|
291
|
+
application has been loaded. This can be done in an initializer.
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
Rails.application.config.after_initialize do
|
295
|
+
ActionAuth.configure do |config|
|
296
|
+
config.sms_send_class = SmsSender
|
297
|
+
end
|
298
|
+
end
|
299
|
+
```
|
300
|
+
|
248
301
|
## Account Deletion
|
249
302
|
|
250
303
|
Account deletion is a feature that is enabled by default. When a user deletes their account, the account
|
@@ -54,6 +54,8 @@ body {
|
|
54
54
|
|
55
55
|
input[type="text"],
|
56
56
|
input[type="email"],
|
57
|
+
input[type="number"],
|
58
|
+
input[type="tel"],
|
57
59
|
input[type="password"] {
|
58
60
|
-webkit-text-size-adjust: 100%;
|
59
61
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
@@ -186,6 +188,8 @@ input[type="password"] {
|
|
186
188
|
|
187
189
|
input[type="text"],
|
188
190
|
input[type="email"],
|
191
|
+
input[type="number"],
|
192
|
+
input[type="tel"],
|
189
193
|
input[type="password"] {
|
190
194
|
color: #d5c4a1;
|
191
195
|
background-color: #282828;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActionAuth
|
2
|
+
class SmsAuths::RequestsController < ApplicationController
|
3
|
+
def new
|
4
|
+
end
|
5
|
+
|
6
|
+
def create
|
7
|
+
@user = User.find_or_initialize_by(phone_number: params[:phone_number])
|
8
|
+
if @user.new_record?
|
9
|
+
password = SecureRandom.hex(32)
|
10
|
+
|
11
|
+
@user.email = "#{SecureRandom.hex(32)}@smsauth"
|
12
|
+
@user.password = password
|
13
|
+
@user.password_confirmation = password
|
14
|
+
@user.verified = false
|
15
|
+
|
16
|
+
@user.save!
|
17
|
+
end
|
18
|
+
|
19
|
+
code = rand(100_000..999_999)
|
20
|
+
@user.update!(sms_code: code, sms_code_sent_at: Time.current)
|
21
|
+
cookies.signed[:sms_auth_phone_number] = { value: @user.phone_number, httponly: true }
|
22
|
+
|
23
|
+
if defined?(ActionAuth.configuration.sms_send_class) &&
|
24
|
+
ActionAuth.configuration.sms_send_class.respond_to?(:send_code)
|
25
|
+
ActionAuth.configuration.sms_send_class.send_code(@user.phone_number, code)
|
26
|
+
end
|
27
|
+
|
28
|
+
redirect_to sms_auths_sign_ins_path, notice: "Check your phone for a SMS Code."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActionAuth
|
2
|
+
class SmsAuths::SignInsController < ApplicationController
|
3
|
+
def show
|
4
|
+
@user = ActionAuth::User.find_by(phone_number: params[:phone_number])
|
5
|
+
end
|
6
|
+
|
7
|
+
def create
|
8
|
+
user = ActionAuth::User.find_by(
|
9
|
+
phone_number: cookies.signed[:sms_auth_phone_number],
|
10
|
+
sms_code: params[:sms_code],
|
11
|
+
sms_code_sent_at: 5.minutes.ago..Time.current
|
12
|
+
)
|
13
|
+
if user
|
14
|
+
@session = user.sessions.create
|
15
|
+
cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
|
16
|
+
user.update(
|
17
|
+
verified: true,
|
18
|
+
sms_code: nil,
|
19
|
+
sms_code_sent_at: nil
|
20
|
+
)
|
21
|
+
redirect_to main_app.root_path, notice: "Signed In"
|
22
|
+
else
|
23
|
+
redirect_to sign_in_path, alert: "Authentication failed, please try again."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -26,7 +26,15 @@ module ActionAuth
|
|
26
26
|
|
27
27
|
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
28
28
|
validates :password, allow_nil: true, length: { minimum: 12 }
|
29
|
-
|
29
|
+
validates :phone_number,
|
30
|
+
allow_nil: true,
|
31
|
+
uniqueness: true,
|
32
|
+
format: {
|
33
|
+
with: /\A\+\d+\z/,
|
34
|
+
message: "must be a valid international phone number with digits only after the country code"
|
35
|
+
}
|
36
|
+
|
37
|
+
normalizes :phone_number, with: -> phone_number { phone_number.gsub(/[^+\d]/, '') }
|
30
38
|
normalizes :email, with: -> email { email.strip.downcase }
|
31
39
|
|
32
40
|
before_validation if: :email_changed?, on: :update do
|
@@ -39,6 +39,9 @@
|
|
39
39
|
<% if ActionAuth.configuration.magic_link_enabled? %>
|
40
40
|
<%= link_to "Magic Link", new_magics_requests_path %> |
|
41
41
|
<% end %>
|
42
|
+
<% if ActionAuth.configuration.sms_auth_enabled? %>
|
43
|
+
<%= link_to "SMS Auth", new_sms_auths_requests_path %> |
|
44
|
+
<% end %>
|
42
45
|
<% if ActionAuth.configuration.passkey_only? %>
|
43
46
|
<%= link_to "Passkey", new_sessions_passkey_path %> |
|
44
47
|
<% end %>
|
@@ -20,6 +20,10 @@
|
|
20
20
|
<span class="mx-3">or</span>
|
21
21
|
<%= link_to "Magic Link", new_magics_requests_path %>
|
22
22
|
<% end %>
|
23
|
+
<% if ActionAuth.configuration.sms_auth_enabled? %>
|
24
|
+
<span class="mx-3">or</span>
|
25
|
+
<%= link_to "SMS Auth", new_sms_auths_requests_path %> |
|
26
|
+
<% end %>
|
23
27
|
<% if ActionAuth.configuration.passkey_only? %>
|
24
28
|
<span class="mx-3">or</span>
|
25
29
|
<%= link_to "Passkey", new_sessions_passkey_path %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<h1>Request SMS Code</h1>
|
2
|
+
|
3
|
+
<%= form_with(url: sms_auths_requests_path) do |form| %>
|
4
|
+
<div class="mb-3">
|
5
|
+
<%= form.label :phone_number, style: "display: block" %>
|
6
|
+
<%= form.telephone_field :phone_number, required: true, autofocus: true, autocomplete: "mobile" %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="mb-3">
|
10
|
+
<%= form.submit "Request SMS Code", class: "btn btn-primary" %>
|
11
|
+
<span class="mx-3">or</span>
|
12
|
+
<%= link_to "Sign In", sign_in_path %>
|
13
|
+
<% if ActionAuth.configuration.passkey_only? %>
|
14
|
+
<span class="mx-3">or</span>
|
15
|
+
<%= link_to "Passkey", new_sessions_passkey_path %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<div class="mb-3">
|
21
|
+
<%= link_to "Sign Up", sign_up_path %> |
|
22
|
+
<%= link_to "Reset Password", new_identity_password_reset_path %>
|
23
|
+
<% if ActionAuth.configuration.verify_email_on_sign_in %>
|
24
|
+
| <%= link_to "Verify Email", identity_email_verification_path %>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<h1>Verify SMS Code</h1>
|
2
|
+
|
3
|
+
<%= form_with(url: sms_auths_sign_ins_path) do |form| %>
|
4
|
+
<%= form.hidden_field :phone_number, value: cookies.signed[:sms_auth_phone_number] %>
|
5
|
+
<div class="mb-3">
|
6
|
+
<%= form.label :sms_code, style: "display: block" %>
|
7
|
+
<%= form.number_field :sms_code, required: true, autofocus: true, autocomplete: "one-time-code" %>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="mb-3">
|
11
|
+
<%= form.submit "Sign In", class: "btn btn-primary" %>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
|
data/config/routes.rb
CHANGED
@@ -35,4 +35,11 @@ ActionAuth::Engine.routes.draw do
|
|
35
35
|
resource :requests, only: [:new, :create]
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
if ActionAuth.configuration.sms_auth_enabled?
|
40
|
+
namespace :sms_auths do
|
41
|
+
resource :sign_ins, only: [:show, :create]
|
42
|
+
resource :requests, only: [:new, :create]
|
43
|
+
end
|
44
|
+
end
|
38
45
|
end
|
@@ -6,6 +6,8 @@ module ActionAuth
|
|
6
6
|
attr_accessor :magic_link_enabled
|
7
7
|
attr_accessor :passkey_only
|
8
8
|
attr_accessor :pwned_enabled
|
9
|
+
attr_accessor :sms_auth_enabled
|
10
|
+
attr_accessor :sms_send_class
|
9
11
|
attr_accessor :verify_email_on_sign_in
|
10
12
|
attr_accessor :webauthn_enabled
|
11
13
|
attr_accessor :webauthn_origin
|
@@ -18,6 +20,8 @@ module ActionAuth
|
|
18
20
|
@magic_link_enabled = true
|
19
21
|
@passkey_only = true
|
20
22
|
@pwned_enabled = defined?(Pwned)
|
23
|
+
@sms_auth_enabled = false
|
24
|
+
@sms_send_class = nil
|
21
25
|
@verify_email_on_sign_in = true
|
22
26
|
@webauthn_enabled = defined?(WebAuthn)
|
23
27
|
@webauthn_origin = "http://localhost:3000"
|
@@ -32,6 +36,10 @@ module ActionAuth
|
|
32
36
|
@magic_link_enabled == true
|
33
37
|
end
|
34
38
|
|
39
|
+
def sms_auth_enabled?
|
40
|
+
@sms_auth_enabled == true
|
41
|
+
end
|
42
|
+
|
35
43
|
def passkey_only?
|
36
44
|
webauthn_enabled? && @passkey_only == true
|
37
45
|
end
|
data/lib/action_auth/version.rb
CHANGED
@@ -13,11 +13,18 @@ namespace :action_auth do
|
|
13
13
|
# config.magic_link_enabled = true
|
14
14
|
# config.passkey_only = true # Allows sign in with only a passkey
|
15
15
|
# config.pwned_enabled = true # defined?(Pwned)
|
16
|
+
# config.sms_auth_enabled = false
|
16
17
|
# config.verify_email_on_sign_in = true
|
17
18
|
# config.webauthn_enabled = true # defined?(WebAuthn)
|
18
19
|
# config.webauthn_origin = "http://localhost:3000" # or "https://example.com"
|
19
20
|
# config.webauthn_rp_name = Rails.application.class.to_s.deconstantize
|
20
21
|
# end
|
22
|
+
#
|
23
|
+
# Rails.application.config.after_initialize do
|
24
|
+
# ActionAuth.configure do |config|
|
25
|
+
# config.sms_send_class = SmsSender
|
26
|
+
# end
|
27
|
+
# end
|
21
28
|
RUBY
|
22
29
|
end
|
23
30
|
puts "Created config/initializers/action_auth.rb"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Kimura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -62,6 +62,8 @@ files:
|
|
62
62
|
- app/controllers/action_auth/registrations_controller.rb
|
63
63
|
- app/controllers/action_auth/sessions/passkeys_controller.rb
|
64
64
|
- app/controllers/action_auth/sessions_controller.rb
|
65
|
+
- app/controllers/action_auth/sms_auths/requests_controller.rb
|
66
|
+
- app/controllers/action_auth/sms_auths/sign_ins_controller.rb
|
65
67
|
- app/controllers/action_auth/users_controller.rb
|
66
68
|
- app/controllers/action_auth/webauthn_credential_authentications_controller.rb
|
67
69
|
- app/controllers/action_auth/webauthn_credentials_controller.rb
|
@@ -83,6 +85,8 @@ files:
|
|
83
85
|
- app/views/action_auth/sessions/index.html.erb
|
84
86
|
- app/views/action_auth/sessions/new.html.erb
|
85
87
|
- app/views/action_auth/sessions/passkeys/new.html.erb
|
88
|
+
- app/views/action_auth/sms_auths/requests/new.html.erb
|
89
|
+
- app/views/action_auth/sms_auths/sign_ins/show.html.erb
|
86
90
|
- app/views/action_auth/user_mailer/email_verification.html.erb
|
87
91
|
- app/views/action_auth/user_mailer/email_verification.text.erb
|
88
92
|
- app/views/action_auth/user_mailer/magic_link.html.erb
|
@@ -99,6 +103,7 @@ files:
|
|
99
103
|
- db/migrate/20240111125859_add_webauthn_credentials.rb
|
100
104
|
- db/migrate/20240111142545_add_webauthn_id_to_users.rb
|
101
105
|
- db/migrate/20240818032321_add_type_to_webauthn_credentials.rb
|
106
|
+
- db/migrate/20241020172209_add_phone_number_to_users.rb
|
102
107
|
- lib/action_auth.rb
|
103
108
|
- lib/action_auth/configuration.rb
|
104
109
|
- lib/action_auth/controllers/helpers.rb
|
@@ -128,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
133
|
- !ruby/object:Gem::Version
|
129
134
|
version: '0'
|
130
135
|
requirements: []
|
131
|
-
rubygems_version: 3.5.
|
136
|
+
rubygems_version: 3.5.22
|
132
137
|
signing_key:
|
133
138
|
specification_version: 4
|
134
139
|
summary: A simple Rails engine for authorization.
|