Skip to content

Commit

Permalink
SMS Authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
kobaltz committed Oct 24, 2024
1 parent 79274ef commit 970283b
Show file tree
Hide file tree
Showing 27 changed files with 429 additions and 66 deletions.
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gemspec

gem "puma"
gem "sqlite3", "~> 1.7"
gem "sqlite3"
gem "sprockets-rails"

group :development do
Expand All @@ -22,3 +22,5 @@ gem "webauthn"

# Add these gems for pwened password support
gem "pwned"

gem "twilio-ruby"
137 changes: 76 additions & 61 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
PATH
remote: .
specs:
action_auth (1.6.0)
action_auth (1.7.0)
bcrypt (~> 3.1.0)
rails (>= 7.1)

GEM
remote: https://rubygems.org/
specs:
actioncable (7.2.1)
actionpack (= 7.2.1)
activesupport (= 7.2.1)
actioncable (7.2.1.2)
actionpack (= 7.2.1.2)
activesupport (= 7.2.1.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (7.2.1)
actionpack (= 7.2.1)
activejob (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
actionmailbox (7.2.1.2)
actionpack (= 7.2.1.2)
activejob (= 7.2.1.2)
activerecord (= 7.2.1.2)
activestorage (= 7.2.1.2)
activesupport (= 7.2.1.2)
mail (>= 2.8.0)
actionmailer (7.2.1)
actionpack (= 7.2.1)
actionview (= 7.2.1)
activejob (= 7.2.1)
activesupport (= 7.2.1)
actionmailer (7.2.1.2)
actionpack (= 7.2.1.2)
actionview (= 7.2.1.2)
activejob (= 7.2.1.2)
activesupport (= 7.2.1.2)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (7.2.1)
actionview (= 7.2.1)
activesupport (= 7.2.1)
actionpack (7.2.1.2)
actionview (= 7.2.1.2)
activesupport (= 7.2.1.2)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4, < 3.2)
Expand All @@ -39,35 +39,35 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (7.2.1)
actionpack (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
actiontext (7.2.1.2)
actionpack (= 7.2.1.2)
activerecord (= 7.2.1.2)
activestorage (= 7.2.1.2)
activesupport (= 7.2.1.2)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.2.1)
activesupport (= 7.2.1)
actionview (7.2.1.2)
activesupport (= 7.2.1.2)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (7.2.1)
activesupport (= 7.2.1)
activejob (7.2.1.2)
activesupport (= 7.2.1.2)
globalid (>= 0.3.6)
activemodel (7.2.1)
activesupport (= 7.2.1)
activerecord (7.2.1)
activemodel (= 7.2.1)
activesupport (= 7.2.1)
activemodel (7.2.1.2)
activesupport (= 7.2.1.2)
activerecord (7.2.1.2)
activemodel (= 7.2.1.2)
activesupport (= 7.2.1.2)
timeout (>= 0.4.0)
activestorage (7.2.1)
actionpack (= 7.2.1)
activejob (= 7.2.1)
activerecord (= 7.2.1)
activesupport (= 7.2.1)
activestorage (7.2.1.2)
actionpack (= 7.2.1.2)
activejob (= 7.2.1.2)
activerecord (= 7.2.1.2)
activesupport (= 7.2.1.2)
marcel (~> 1.0)
activesupport (7.2.1)
activesupport (7.2.1.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
Expand Down Expand Up @@ -100,6 +100,12 @@ GEM
docile (1.4.1)
drb (2.2.1)
erubi (1.13.0)
faraday (2.12.0)
faraday-net_http (>= 2.0, < 3.4)
json
logger
faraday-net_http (3.3.0)
net-http
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.6)
Expand All @@ -108,6 +114,7 @@ GEM
irb (1.14.1)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
json (2.7.2)
jwt (2.9.3)
base64
launchy (3.0.1)
Expand All @@ -128,7 +135,9 @@ GEM
mini_mime (1.1.5)
minitest (5.25.1)
minitest-stub_any_instance (1.0.3)
net-imap (0.4.16)
net-http (0.4.1)
uri
net-imap (0.5.0)
date
net-protocol
net-pop (0.1.2)
Expand All @@ -154,38 +163,38 @@ GEM
nio4r (~> 2.0)
pwned (2.4.1)
racc (1.8.1)
rack (3.1.7)
rack (3.1.8)
rack-session (2.0.0)
rack (>= 3.0.0)
rack-test (2.1.0)
rack (>= 1.3)
rackup (2.1.0)
rack (>= 3)
webrick (~> 1.8)
rails (7.2.1)
actioncable (= 7.2.1)
actionmailbox (= 7.2.1)
actionmailer (= 7.2.1)
actionpack (= 7.2.1)
actiontext (= 7.2.1)
actionview (= 7.2.1)
activejob (= 7.2.1)
activemodel (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
rails (7.2.1.2)
actioncable (= 7.2.1.2)
actionmailbox (= 7.2.1.2)
actionmailer (= 7.2.1.2)
actionpack (= 7.2.1.2)
actiontext (= 7.2.1.2)
actionview (= 7.2.1.2)
activejob (= 7.2.1.2)
activemodel (= 7.2.1.2)
activerecord (= 7.2.1.2)
activestorage (= 7.2.1.2)
activesupport (= 7.2.1.2)
bundler (>= 1.15.0)
railties (= 7.2.1)
railties (= 7.2.1.2)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.2.1)
actionpack (= 7.2.1)
activesupport (= 7.2.1)
railties (7.2.1.2)
actionpack (= 7.2.1.2)
activesupport (= 7.2.1.2)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
Expand All @@ -212,18 +221,23 @@ GEM
actionpack (>= 6.1)
activesupport (>= 6.1)
sprockets (>= 3.0.0)
sqlite3 (1.7.3-aarch64-linux)
sqlite3 (1.7.3-arm64-darwin)
sqlite3 (1.7.3-x86_64-linux)
sqlite3 (2.1.1-aarch64-linux-gnu)
sqlite3 (2.1.1-arm64-darwin)
sqlite3 (2.1.1-x86_64-linux-gnu)
stringio (3.1.1)
thor (1.3.2)
timeout (0.4.1)
tpm-key_attestation (0.12.1)
bindata (~> 2.4)
openssl (> 2.0)
openssl-signature_algorithm (~> 1.0)
twilio-ruby (7.3.4)
faraday (>= 0.9, < 3.0)
jwt (>= 1.5, < 3.0)
nokogiri (>= 1.6, < 2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uri (0.13.1)
useragent (0.16.10)
webauthn (3.1.0)
android_key_attestation (~> 0.3.0)
Expand All @@ -238,7 +252,7 @@ GEM
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.6.18)
zeitwerk (2.7.1)

PLATFORMS
aarch64-linux
Expand All @@ -254,7 +268,8 @@ DEPENDENCIES
pwned
simplecov
sprockets-rails
sqlite3 (~> 1.7)
sqlite3
twilio-ruby
webauthn

BUNDLED WITH
Expand Down
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,18 @@ ActionAuth.configure do |config|
config.magic_link_enabled = true
config.passkey_only = true # Allows sign in with only a passkey
config.pwned_enabled = true # defined?(Pwned)
config.sms_auth_enabled = false
config.verify_email_on_sign_in = true
config.webauthn_enabled = true # defined?(WebAuthn)
config.webauthn_origin = "http://localhost:3000" # or "https://example.com"
config.webauthn_rp_name = Rails.application.class.to_s.deconstantize
end
Rails.application.config.after_initialize do
ActionAuth.configure do |config|
config.sms_send_class = SmsSender
end
end
```
## Features
Expand All @@ -152,6 +159,8 @@ These are the planned features for ActionAuth. The ones that are checked off are
⏳ - OAuth with Google, Facebook, Github, Twitter, etc.
✅ - SMS Authentication
✅ - Have I Been Pwned Integration
✅ - Account Deletion
Expand Down Expand Up @@ -245,6 +254,50 @@ an email to the user with a link that will log them in. This is a great way to a
without having to remember a password. This is especially useful for users who may not have a password
manager or have a hard time remembering passwords.

### SMS Authentication

SMS Authentication is disabled by default. The purpose of this is to allow users to authenticate
with a phone number. This is useful and specific to applications that may require a phone number
instead of an email address for authentication. The basic workflow for this is to register a phone
number, and then send a code to the phone number. The user will then enter the code to authenticate.

No password or email is required for this. I do not recommend enabling this feature for most applications.

You must set up your own SMS Provider. This is not included in the gem. You will need to configure the
`sms_send_class` to send the SMS code. This will expect a class method called `send_code` that will take in the parameters
`phone_number` and `code`.

```ruby
require 'twilio-ruby'
class SmsSender
def self.send_code(phone_number, code)
account_sid = ENV['TWILIO_ACCOUNT_SID']
auth_token = ENV['TWILIO_AUTH_TOKEN']
from_number = ENV['TWILIO_PHONE_NUMBER']
client = Twilio::REST::Client.new(account_sid, auth_token)
client.messages.create(
from: from_number,
to: phone_number,
body: "Your verification code is #{code}"
)
end
end
```

Since this file could live in the `app/models` or elsewhere, we will need to set its configuration after the Rails
application has been loaded. This can be done in an initializer.

```ruby
Rails.application.config.after_initialize do
ActionAuth.configure do |config|
config.sms_send_class = SmsSender
end
end
```

## Account Deletion

Account deletion is a feature that is enabled by default. When a user deletes their account, the account
Expand Down
4 changes: 4 additions & 0 deletions app/assets/stylesheets/action_auth/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ body {

input[type="text"],
input[type="email"],
input[type="number"],
input[type="tel"],
input[type="password"] {
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
Expand Down Expand Up @@ -186,6 +188,8 @@ input[type="password"] {

input[type="text"],
input[type="email"],
input[type="number"],
input[type="tel"],
input[type="password"] {
color: #d5c4a1;
background-color: #282828;
Expand Down
31 changes: 31 additions & 0 deletions app/controllers/action_auth/sms_auths/requests_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module ActionAuth
class SmsAuths::RequestsController < ApplicationController
def new
end

def create
@user = User.find_or_initialize_by(phone_number: params[:phone_number])
if @user.new_record?
password = SecureRandom.hex(32)

@user.email = "#{SecureRandom.hex(32)}@smsauth"
@user.password = password
@user.password_confirmation = password
@user.verified = false

@user.save!
end

code = rand(100_000..999_999)
@user.update!(sms_code: code, sms_code_sent_at: Time.current)
cookies.signed[:sms_auth_phone_number] = { value: @user.phone_number, httponly: true }

if defined?(ActionAuth.configuration.sms_send_class) &&
ActionAuth.configuration.sms_send_class.respond_to?(:send_code)
ActionAuth.configuration.sms_send_class.send_code(@user.phone_number, code)
end

redirect_to sms_auths_sign_ins_path, notice: "Check your phone for a SMS Code."
end
end
end
Loading

0 comments on commit 970283b

Please sign in to comment.