Skip to content

Instantly share code, notes, and snippets.

@artyomlagun
Last active October 17, 2023 14:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save artyomlagun/b6940ab954588fcd35d750fcbc706931 to your computer and use it in GitHub Desktop.
Save artyomlagun/b6940ab954588fcd35d750fcbc706931 to your computer and use it in GitHub Desktop.
Send OTP
class PasscodeService
# assume that phone can contain from 7 to 9 digits
PHONE_REGEXP = /^\d{7,9}$/
def initialize
@response = { action: false, message: '' }
end
def send(phone)
# validate phone through regexp
unless PHONE_REGEXP.match?(phone.to_s)
@response[:message] = 'Phone is invalid'
return @response
end
if user.present?
SendPasscodeWorker.deliver(phone, user.generate_otp)
@response[:action] = true
else
@response[:message] = 'User not found'
end
@response
end
def verify(phone, otp)
if user.present?
@response[:action] = user.verify_otp(otp)
@response[:message] = 'Invalid OTP' unless user.verify_otp(otp)
else
@response[:messgae] = 'User not found'
end
@response
end
private
def user
User.find_by_phone(phone)
end
end
# User:
# id
# email
# phone
# otp_secret
# last_otp_at
# for OTP we can use this gem: https://github.com/gotoyuzo/otp
# it's old, but not sure if we would have any critical issues with it
require 'otp'
class User < ApplicationRecord
def generate_otp
# create TOTP instance and new_secret
totp = OTP::TOTP.new
totp.new_secret
# update otp_secret and last_otp_at fields for current user
update_otp_fields!(totp.secret)
# return OTP
totp.password
end
def verify_otp(otp)
totp = OTP::TOTP.new
totp.secret = otp_secret
totp.verify(otp)
end
def update_otp_fields!(secret)
update!(otp_secret: secret, last_otp_at: Time.now)
end
end
# in routes.rb
# resources :users do
# member do
# post :send_otp
# post :verify_otp
# end
# end
class UsersController < ApplicationController
def send_otp
response = PasscodeService.new.send(params[:phone])
if response[:action]
render json: { is_sent: true }, status: :ok
else
redner json: { is_sent: false, message: response[:message] }, status: :bad_request
end
end
def verify_otp
response = PasscodeService.new.verify(params[:phone], params[:otp])
if response[:action]
render json: { is_valid: true }, status: :ok
else
render json: { is_valid: false, message: response[:message] }, status: :bad_request
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment