Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Devise sign up either by email or by mobile number

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 authentication key. So that if a user provides email address, it must be saved in email field in database and if user provides mobile number, it must be saved in mobile field in database.

And also I would like to overwrite the confirmation method of mobile user and would like to send an message with activation key and inter the key in the application to activate registration. I think its not so hard, but I didn't get where to start. Please suggest me the favourable way to accomplish this task.

like image 649
Santosh Aryal Avatar asked Jan 05 '23 18:01

Santosh Aryal


2 Answers

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

like image 197
Hardik Upadhyay Avatar answered Jan 15 '23 13:01

Hardik Upadhyay


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.

like image 34
Santosh Aryal Avatar answered Jan 15 '23 11:01

Santosh Aryal