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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0224e818bf936f4cd37a67bc6634e95d600774962f07796f88b7e66471273a8
4
- data.tar.gz: 95281cb2e34de5fc9f91dcfb1eaca05263ebdedcc5ce4fec14223f27e2d6cf21
3
+ metadata.gz: 2fa128174c9685bef3e348f40b7bfd48c28e26c23ba7c51d3e7088ce039d939a
4
+ data.tar.gz: d92884ccd4e77112736f5f6fd9753def05508d1eb0135cb103af6b7c9a392c62
5
5
  SHA512:
6
- metadata.gz: de4c8862b0a7ffdc2688c02d6e5b0cd2120b16d255dc2a63d5dfb2962cd99714ffd77bd80f41d2151f763fc6da44f025cb5092522d12d705db5b712fc88bb654
7
- data.tar.gz: 7a2f3e28ae99f38d58955a3381e6aec6590c0f463b3e7d3d7294221f74bdb3580b2dbeed62d0148aca7346f3a3c7c9a59ce300d5d078b03053b7c08cb2cf3278
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
@@ -0,0 +1,7 @@
1
+ class AddPhoneNumberToUsers < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :users, :phone_number, :string
4
+ add_column :users, :sms_code, :string
5
+ add_column :users, :sms_code_sent_at, :datetime
6
+ end
7
+ 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
@@ -1,3 +1,3 @@
1
1
  module ActionAuth
2
- VERSION = "1.6.0"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -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.6.0
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-09-28 00:00:00.000000000 Z
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.20
136
+ rubygems_version: 3.5.22
132
137
  signing_key:
133
138
  specification_version: 4
134
139
  summary: A simple Rails engine for authorization.