I can register a user getting both email address and mobile number in my application. But what I really want is to register user using either email or mobile as a primary authen
Yes, you can do easily with minor setting.
like this
Modify your application_controller.rb
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
added_attrs = [:mobile_no, :email, :password, :password_confirmation, :remember_me]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
end
Create a login virtual attribute in the User model
Add login as an attr_accessor:
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
or, if you will use this variable somewhere else in the code:
def login=(login)
@login = login
end
def login
@login || self.mobile_no || self.email
end
Modify config/initializers/devise.rb to have:
config.authentication_keys = [ :login ]
You can refer this link for more reference.
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address
Aha! I did it. I followed Devise sign in using username or email address, I changed username to mobile and was able to sign in using the mobile number. But the problem I figured is that I was unable to sign up with mobile. So, based on the suggestion of @Hardik, I used some jQuery in my sign up form to accept either mobile or email address.
Since I have allowed Users to set their password themselves after confirming their email address, it became a problem for the mobile number so I checked for the presence of email in request and skipped the confirmation and set a dynamically generated password which I had declared as a instance variable on registrations_controller.rb file which which inherits form Devise Registrations Controller.
User Model
before_save :check_email
def check_email
if !self.email.present?
skip_confirmation!
end
end
Hence, I am able to skip confirmation and I provide a default password.
Now the application accepts both email address and mobile number. User who register with email address can set their password but for the mobile users, they cannot set their password. So, I used Pilvo SMS api to send their password in their mobile number. For this I used the Pilvo gem and their API and overwrite the Devise Registration Controller.
Gemfile
gem 'phonelib'
gem 'plivo', '~> 0.3.19'
I used Phonelib for the validation of Mobile Numbers.
require 'rubygems'
require 'plivo'
include Plivo
class RegistrationsController < Devise::RegistrationsController
prepend_before_action :require_no_authentication, only: [:new, :create, :cancel]
prepend_before_action :authenticate_scope!, only: [:edit, :update, :destroy]
prepend_before_action :set_minimum_password_length, only: [:new, :edit]
AUTH_ID = "PLIVO AUTH ID"
AUTH_TOKEN = "PLIVO AUTH TOKEN"
# GET /resource/sign_up
def new
super
end
# POST /resource
def create
if !sign_up_params[:email].present?
@password = Devise.friendly_token.first(8)
#begin register
build_resource
resource.password = @password
resource.mobile = sign_up_params[:mobile]
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up_mobile if is_navigational_format?
#redirect_to root_path
respond_with resource, :location => after_sign_up_path_for(resource)
p = RestAPI.new(AUTH_ID, AUTH_TOKEN)
# Send SMS
params = {
'src' => '+9779855065526', # Sender's phone number with country code
'dst' => '+'+ sign_up_params[:mobile].to_s, # Receiver's phone Number with country code
'text' => t('sms.register', :password => @password.to_s).html_safe, # Your SMS Text Message - English
'method' => 'POST' # The method used to call the url
}
response = p.send_message(params)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
respond_with resource
end
else
super
end
end
protected
def after_sign_up_path_for(resource)
root_path # Or :prefix_to_your_route
end
def after_update_path_for(resource)
if admin?
control_index_path
elsif merchant?
merchants_path
elsif customer?
customers_path
end
end
end
It worked, and I am getting SMS but I am not sure if it is a good or secure approach or not. If this is not a secure approach, please suggest me a favourable way.