Module: Auth::Concerns::OtpConcern

Extended by:
ActiveSupport::Concern
Defined in:
app/controllers/auth/concerns/otp_concern.rb

Instance Method Summary collapse

Instance Method Details

#initialize_varsObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'app/controllers/auth/concerns/otp_concern.rb', line 13

def initialize_vars
  	##deep symbolize the incoming params after passing through permitted params.
  	
  	@resource_params = permitted_params
  	
  	puts "the resoure params are:"
  	puts @resource_params.to_s

  	##if the resource is defined, assign the class and the symbol for use further in the file
  	##eg: resource is provided in the route as : users, so 
  	##@resource_class => User
  	##@resource_symbol =>  :user

  	if collection = @resource_params[:resource]
  		##check that the resource exists in the auth_configuration
  		if Auth.configuration.auth_resources[collection.singularize.capitalize]
  			@resource_class = collection.singularize.capitalize.constantize
  			@resource_symbol = collection.singularize.to_sym
  					
  			
  			##this is either the provided email(in case of forgot_password form, we pass in the additional_login_param under the email key itself.#ref auth/modals/forgot_password_content.html.erb)
  			if @resource_params[@resource_symbol]
			  	@additional_login_param = @resource_params[@resource_symbol][:email] || @resource_params[@resource_symbol][:additional_login_param]
			  	
			  	

			  	##the otp provided by the user, only used in the verify_otp action.
			  	@otp = @resource_params[@resource_symbol][:otp]
			  	
			  	##the resource_id of the user, only used in the short_polling endpoint.
			  	@resource_id = @resource_params[@resource_symbol][:_id]
			  	
  			end

  		else
  			##have to have some way of showing these errors.
  			not_found("provided resource not found in app")
  		end
  	else
  		not_found("no resource collection provided")
  	end
  	
  	##the intent , passed into the send_sms_otp endpoint, and thereafter added to the verify_otp_path, in the new_otp_input.html.erb
  	##set as default to empty so that it doesnt screw up in the partials, screaming undefined.
  	@intent = @resource_params[:intent] or ""
  	
  	
  	##the default response status, can be changed in the action depending on individual situations.
  	@response_status = 200
end

#otp_verification_resultObject

SHORT-POLLING ENDPOINT TO DETERMINE IF THE OTP WAS VALID. CALLED IN THE POST-VERIFICATION PAGE. error returns for this def are different, because it is requested using json from the verify_otp.js.erb partial as a result, in case there is an error in the controller action itslef below(eg. wherever there is a 422/400), then the following happens. spinner.js //catches any non 200/201 status and interprets it as an error //thereafter directly show_error_modal is called. //i could have written logic specific for otp_verification_result, by checking if it is there in the request_url, but did not do so, because otp is not always going to be in the engine, so otp should not be hardcoded anywhere. //the error lands up being shown inside show_error_modal, by means of json parsing the incoming string, and showing json as the error message. on the other hand, if there is any othe rtype of error in the before_filter initialize_vars, then that raises a not_found and is handled by rendering a json response with errors, and a 422 so again it is handled by spinner as above.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'app/controllers/auth/concerns/otp_concern.rb', line 147

def otp_verification_result

		## a message to display on the website after the otp is successfully verified in case of forgot_password or unlock_account.
 	intent_verification_message = nil
 	res_verified = false
 	##first check the errors
 	

 	if resource = @resource_class.where(:additional_login_param => @additional_login_param, :otp => @otp).first
 		
 		if otp_error = resource.check_otp_errors
 			@status = 422
  		resource.errors.add(:additional_login_param,otp_error)
 		else
 			resource.m_client = self.m_client
			resource.set_client_authentication	
	  	if resource. 
		  	puts "resource additional login param is confirmed."
		  	puts "intent is: #{@intent}"
		  	if @intent == "reset_password"
		  		
		  		##protected method so had to do this.
		  		if resource.confirmed? && !resource.pending_reconfirmation?
		  			resource.class.send_reset_password_instructions(resource.attributes)
		  			
		  			intent_verification_message = "An email has been sent to your email account, with instructions on resetting your password" if resource.errors.empty?
		  			

		  			
		  			##if successfull_sent ->
		  			##else
		  			## here error is added anyway to resource.
		  			##end
		  			##we want to send the reset password instructions, but using the email.
		  		else
		  			
		  			resource.errors.add(:additional_login_param,"you do not have a confirmed email account set for this account, you cannot recover the password.")
		  			
		  			@status = 400
		  		end
		  		#raw_token = resource.send(:set_reset_password_token)
		  		#intent_url = send("edit_#{@resource_symbol.to_s}_password_path",{:reset_password_token => raw_token})
		  	elsif @intent == "unlock_account"
		  		##here normally would be resource.unlock.
		  		##code from https://github.com/plataformatec/devise/blob/master/lib/devise/models/lockable.rb#send_unlock_instructions
		  		#puts "came to unlocks."
		  		
		  		#raw, enc = Devise.token_generator.generate(@resource.class, :unlock_token)
       			#@resource.unlock_token = enc
       			#@resource.save(validate: false)
				


				if resource.confirmed? && !resource.	pending_reconfirmation?

					resource.send_unlock_instructions 

					intent_verification_message = "An email has been sent to your email account, with instructions on unlocking your account" if resource.errors.empty?
				else
					

       				resource.errors.add(:additional_login_param,"cannot send unlock instructions because you dont have a confirmed email address.")

       		    end
		  				        			
       			#intent_url = send("#{@resource_symbol.to_s}_unlock_path",{:unlock_token => raw})
		  	end
		  	##make the intent token nil, it can be used only thus once.
		  	
	  	end
	end
else
	resource = @resource_class.new
	@status = 422
	puts "came here."
	resource.errors.add(:additional_login_param,"Either otp or additional login param is incorrect, try resend otp")
end

#puts @resource.attributes.to_s
@auth_user = resource
 	respond_to do |format|
 	  format.json {render json: {:intent_verification_message => intent_verification_message, :errors => resource.errors.full_messages, :resource => resource.as_json({:otp_verification => true}), :verified => (resource. && resource.errors.empty?)}, status: @status}
 	  format.html {render "auth/confirmations/otp_status_result.html.erb"}
 	end
end

#permitted_paramsObject



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'app/controllers/auth/concerns/otp_concern.rb', line 235

def permitted_params
  	if action_name == "resend_sms_otp"
  		##the resource_collection_path => pluralized downcased model name eg. users
  		params.permit(:intent,:resource,:api_key,:current_app_id)
  	else
  		##post_verification_intent => "reset_password" OR "unlock"
  		##had to add email here because in the passwords form, and the unlocks form, we have to serve either additional_login_param or email, so in order to make it work with the existing devise controllers decided to keep the param coming in as email, and sending errors back also on the email attribute,[all this is only relevant to the send_sms_otp action]
  		##it will take all the models provided in the authentication_keys in the Auth configuration file.
  		filters = []
  		Auth.configuration.auth_resources.keys.each do |model|
  			filters << {model.downcase.to_sym => [:additional_login_param, :otp, :email, :_id]}
  		end
  		filters << [:intent, :resource,:api_key,:current_app_id]
  		filters << "g-recaptcha-response".to_sym
  		params.permit(filters)
  	end
end

#resend_sms_otpObject

this is only used for the modal based things has no role otherwise. CALLED WHEN WE WANT TO SHOW THE USER A MODAL TO RE-ENTER HIS MOBILE NUMBER SO THAT WE CAN AGAIN SEND AN OTP TO IT.



99
100
101
102
103
104
105
# File 'app/controllers/auth/concerns/otp_concern.rb', line 99

def resend_sms_otp
	resource = @resource_class.new
	respond_to do |format|
 format.json {render json: resource.to_json, status: @status}
 format.js   {render "auth/confirmations/_resend_otp.js.erb", locals: {resource: resource, intent: @intent}}
		end
end

#send_sms_otpObject

CALLED WHEN THE USER HAS ENTERED HIS MOBILE NUMBER, SO THAT HE GETS ANOTHER OTP



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/controllers/auth/concerns/otp_concern.rb', line 65

def send_sms_otp
	##IF THERE IS AN INTENT,THEN WE MUST HAVE A CONFIRMED ACCOUNT.
	##OTHERWISE WE DONT NEED THAT.
	##WHY?
	##because : suppose that we are calling send_sms_otp from the forgot_password / unlocks controller -> then we have to ensure that the account has been verified.
	##otherwise we cannot send otp's to non-verified phone numbers to do things like reset_passwords / unlock 
	##on the other hand in case there is no intent, like in case of resend_otp -> then we can only check if we have an account with this mobile number or not, no need to check for verification.
	conditions = @intent.blank? ? {:additional_login_param => @additional_login_param} : {:additional_login_param => @additional_login_param, :additional_login_param_status => 2}

	if @additional_login_param.nil?
		@status = 422
		resource = @resource_class.new
		resource.errors.add(:additional_login_param,"Additional login param not provided")
	elsif resource = @resource_class.where(conditions).first
		#resource.intent_token = Devise.friendly_token if [email protected]?
		#resource.save
		resource.m_client = self.m_client
	resource.set_client_authentication
		resource.send_sms_otp
	elsif resource = @resource_class.new
		@status = 422
		resource.errors.add(:additional_login_param,"Could not find a resource with that additional login param")
	end 
	@auth_user = resource
	respond_to do |format|
format.json {render json: resource.to_json({:otp_verification => true}), status: @status}
format.js   {render :partial => "auth/confirmations/new_otp_input.js.erb", locals: {resource: resource, intent: @intent}}
format.html {render 'auth/confirmations/enter_otp.html.erb'}
		end
end

#verify_otpObject

CALLED WHEN THE USER ENTERS THE OTP SENT ON HIS MOBILE VERIFIES THE OTP WITH THE THIRD PARTY API.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'app/controllers/auth/concerns/otp_concern.rb', line 109

def verify_otp
  	if resource = @resource_class.where(:additional_login_param => @additional_login_param).first 
  		resource.m_client = self.m_client
 		resource.set_client_authentication
 		
  		##there are no errors, so we proceed with verification.
  		if otp_error = resource.check_otp_errors
  			@status = 422
  			resource.errors.add(:additional_login_param,otp_error)
  		else
  			resource.verify_sms_otp(@otp)
  			## just setting so that it is available on the resource object.
  			resource.otp = @otp
  		end
  	else
  		resource = @resource_class.new
  		resource.errors.add(:additional_login_param,"Not Found")
  		@status = 400
  	end
  	@auth_user = resource
  	respond_to do |format|
 		  format.json {render json: resource.as_json({:otp_verification => true}), status: @status}
 		  format.js   {render :partial => "auth/confirmations/verify_otp.js.erb", locals: {resource: resource, intent: @intent, otp: @otp}}
 		  format.html {render "auth/confirmations/get_otp_status.html.erb"}
 		end
end