Skip to content
This repository was archived by the owner on Feb 28, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ SMTP_DOMAIN=""
ATHUL_AUTH_TOKEN=""
SIDEKIQ_HTTP_USERNAME=""
SIDEKIQ_HTTP_PASSWORD=""
TWILIO_API_KEY=""
TWILIO_ACCOUNT_SID=""
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ SMTP_DOMAIN=""
ATHUL_AUTH_TOKEN=""
SIDEKIQ_HTTP_USERNAME=""
SIDEKIQ_HTTP_PASSWORD=""
TWILIO_API_KEY=""
TWILIO_ACCOUNT_SID=""
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@
/storage

# Ignore database exports from Heroku
*.dump
*.dump

# Intellij
.idea
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,5 @@ end

gem 'gruff'
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]

gem "twilio-ruby", "~> 5.61"
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ GEM
jmespath (1.4.0)
json (2.1.0)
jsonapi-renderer (0.2.0)
jwt (2.3.0)
launchy (2.4.3)
addressable (~> 2.3)
listen (3.0.8)
Expand Down Expand Up @@ -327,6 +328,10 @@ GEM
thor (1.1.0)
thread_safe (0.3.6)
timezone (1.3.2)
twilio-ruby (5.61.0)
faraday (>= 0.9, < 2.0)
jwt (>= 1.5, <= 2.5)
nokogiri (>= 1.6, < 2.0)
tzinfo (1.2.9)
thread_safe (~> 0.1)
unf (0.1.4)
Expand Down Expand Up @@ -391,6 +396,7 @@ DEPENDENCIES
stripe (~> 3.0)
terminal-table (~> 1.7)
timezone (~> 1.0)
twilio-ruby (~> 5.61)
tzinfo-data
vcr (~> 3.0)
webmock (~> 2.1)
Expand Down
33 changes: 32 additions & 1 deletion app/controllers/v1/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,36 @@ def exchange_login_code
render_success(auth_token: user.auth_token)
end

def sms_auth
user = User.find_by(email: params[:email].downcase)
return render_not_found unless user
return render_field_error(:phone_number, 'no phone number provided') unless user.phone_number

::TwilioVerificationService.new.send_verification_request(user.phone_number)
render_success(
id: user.id,
email: user.email,
status: 'login code sent'
)
end

def sms_exchange_login_code
err = -> { render_field_error(:login_code, 'invalid', 401) }

user = User.find(params[:user_id])
login_code = params[:login_code]

return render_not_found unless user
return err.call if login_code.nil?

return err.call unless ::TwilioVerificationService.new.check_verification_token(user.phone_number, login_code)

user.generate_auth_token!
user.save!

render_success(auth_token: user.auth_token)
end

def current
render_success current_user
end
Expand Down Expand Up @@ -89,7 +119,8 @@ def user_params
:username,
:email_on_new_challenges,
:email_on_new_challenge_posts,
:email_on_new_challenge_post_comments
:email_on_new_challenge_post_comments,
:phone_number
)
end
end
Expand Down
5 changes: 5 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class User < ApplicationRecord
:email_on_new_challenge_post_comments,
inclusion: { in: [true, false] }

validates :phone_number, format: {
with: /\A\+[1-9]\d{1,14}\z/, message: 'must be a valid E.164 phone number',
unless: -> { phone_number.nil? }
}

validate :username_cannot_be_unset

has_many :login_codes
Expand Down
1 change: 1 addition & 0 deletions app/serializers/user_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class UserSerializer < ActiveModel::Serializer
attribute :email_on_new_challenges, if: :logged_in?
attribute :email_on_new_challenge_posts, if: :logged_in?
attribute :email_on_new_challenge_post_comments, if: :logged_in?
attribute :phone_number, if: :logged_in?

belongs_to :new_leader, if: :logged_in?
has_many :leadership_position_invites, if: :logged_in?
Expand Down
28 changes: 28 additions & 0 deletions app/services/twilio_verification_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

require 'twilio-ruby'

class TwilioVerificationService
CLIENT = Twilio::REST::Client.new(
Rails.application.secrets.twilio_account_sid,
Rails.application.secrets.twilio_api_key
)

# This isn't private/sensitive so it's okay to keep here
VERIFY_SERVICE_ID = 'VAa06a66dad4c1ca3c199a46334ff11945'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't private so it's okay to just hardcode it here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment above that line that so someone doesn't put it in secrets later?


def send_verification_request(phone_number)
CLIENT.verify
.services(VERIFY_SERVICE_ID)
.verifications
.create(to: phone_number, channel: 'sms')
end

def check_verification_token(phone_number, code)
verification = CLIENT.verify
.services(VERIFY_SERVICE_ID)
.verification_checks
.create(to: phone_number, code: code)
verification.status == 'approved'
end
end
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@
resources :users, only: %i[index show update] do
collection do
post 'auth'
post 'sms_auth'
get 'current'
end

post 'exchange_login_code'
post 'sms_exchange_login_code'
post 'new_leader', to: 'users/new_leaders#create'

resources :new_club_applications, only: %i[index create]
Expand Down
4 changes: 4 additions & 0 deletions config/secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ common: &common
# MLH Local Hack Day API Key
mlh_lhd_api_key: <%= ENV["MLH_LHD_API_KEY"] %>

# Twilio account
twilio_api_key: <%= ENV["TWILIO_API_KEY"] %>
twilio_account_sid: <%= ENV["TWILIO_ACCOUNT_SID"] %>

development:
<<: *common
secret_key_base: 6d02f9126a44347e07442fddf774b6a434a85b86e23629eab947c9c101b8aa878ad8fe5dadba26a3341f1a16f480068ae5234861cb7b913f526a0e96a21fcc7d
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20211116060739_add_phone_number.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddPhoneNumber < ActiveRecord::Migration[5.2]
def change
add_column :users, :phone_number, :text
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2019_12_20_064914) do
ActiveRecord::Schema.define(version: 2021_11_16_060739) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -635,6 +635,7 @@
t.boolean "email_on_new_challenge_post_comments"
t.bigint "new_leader_id"
t.datetime "shadow_banned_at"
t.text "phone_number"
t.index ["auth_token"], name: "index_users_on_auth_token"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["new_leader_id"], name: "index_users_on_new_leader_id"
Expand Down