cpaas-sdk 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/LICENSE.md +1 -0
- data/README.md +25 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cpaas-sdk.gemspec +32 -0
- data/docs/Cpaas.html +446 -0
- data/docs/Cpaas/Conversation.html +1742 -0
- data/docs/Cpaas/Notification.html +301 -0
- data/docs/Cpaas/TwoFactor.html +908 -0
- data/docs/_index.html +146 -0
- data/docs/_index.md +21 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +496 -0
- data/docs/file.README.html +102 -0
- data/docs/file._index.html +94 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +94 -0
- data/docs/js/app.js +303 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +251 -0
- data/docs/mv_index.html +102 -0
- data/docs/top-level-namespace.html +663 -0
- data/examples/2fa/.env.example +6 -0
- data/examples/2fa/.gitignore +159 -0
- data/examples/2fa/.ruby-gemset +1 -0
- data/examples/2fa/.ruby-version +1 -0
- data/examples/2fa/Gemfile +8 -0
- data/examples/2fa/README.md +34 -0
- data/examples/2fa/app.rb +134 -0
- data/examples/2fa/config.ru +10 -0
- data/examples/2fa/helper.rb +37 -0
- data/examples/2fa/public/stylesheets/forms.css +28 -0
- data/examples/2fa/public/stylesheets/global.css +7 -0
- data/examples/2fa/public/stylesheets/layout.css +45 -0
- data/examples/2fa/public/stylesheets/main.css +3 -0
- data/examples/2fa/views/alert.erb +5 -0
- data/examples/2fa/views/dashboard.erb +4 -0
- data/examples/2fa/views/index.erb +17 -0
- data/examples/2fa/views/login.erb +13 -0
- data/examples/2fa/views/verify.erb +8 -0
- data/examples/sms/.env.example +4 -0
- data/examples/sms/.gitignore +159 -0
- data/examples/sms/.ruby-gemset +1 -0
- data/examples/sms/.ruby-version +1 -0
- data/examples/sms/Gemfile +8 -0
- data/examples/sms/README.md +80 -0
- data/examples/sms/app.rb +87 -0
- data/examples/sms/config.ru +10 -0
- data/examples/sms/helper.rb +33 -0
- data/examples/sms/public/scripts/notification.js +46 -0
- data/examples/sms/public/stylesheets/forms.css +28 -0
- data/examples/sms/public/stylesheets/global.css +7 -0
- data/examples/sms/public/stylesheets/layout.css +74 -0
- data/examples/sms/public/stylesheets/main.css +3 -0
- data/examples/sms/views/alert.erb +5 -0
- data/examples/sms/views/index.erb +48 -0
- data/lib/cpaas-sdk.rb +30 -0
- data/lib/cpaas-sdk/api.rb +139 -0
- data/lib/cpaas-sdk/config.rb +9 -0
- data/lib/cpaas-sdk/resources.rb +4 -0
- data/lib/cpaas-sdk/resources/conversation.rb +268 -0
- data/lib/cpaas-sdk/resources/notification.rb +62 -0
- data/lib/cpaas-sdk/resources/notification_channel.rb +39 -0
- data/lib/cpaas-sdk/resources/twofactor.rb +136 -0
- data/lib/cpaas-sdk/util.rb +93 -0
- data/lib/cpaas-sdk/version.rb +3 -0
- data/tutorials/2FA.md +109 -0
- data/tutorials/2fa-flow.png +0 -0
- data/tutorials/GetStarted.md +86 -0
- data/tutorials/SMSMessaging.md +132 -0
- data/tutorials/index.html +86 -0
- data/tutorials/quickstarts.yml +15 -0
- metadata +238 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'cpaas-sdk/util'
|
2
|
+
|
3
|
+
module Cpaas
|
4
|
+
#
|
5
|
+
# CPaaS notification helper methods
|
6
|
+
#
|
7
|
+
class Notification
|
8
|
+
#
|
9
|
+
# Parse inbound sms notification received in webhook. It parses the notification and returns
|
10
|
+
# simplified version of the response.
|
11
|
+
#
|
12
|
+
# @param notification [Hash] JSON received in the subscription webhook.
|
13
|
+
#
|
14
|
+
def self.parse(notification)
|
15
|
+
parsed_notification = convert_hash_keys(notification)
|
16
|
+
top_level_key = parsed_notification.keys.first
|
17
|
+
notification_obj = parsed_notification[top_level_key]
|
18
|
+
|
19
|
+
case top_level_key
|
20
|
+
when :outbound_sms_message_notification, :inbound_sms_message_notification
|
21
|
+
message = notification_obj.dig(:outbound_sms_message).nil? ? notification_obj.dig(:inbound_sms_message) : notification_obj.dig(:outbound_sms_message)
|
22
|
+
|
23
|
+
{
|
24
|
+
notification_id: notification_obj.dig(:id),
|
25
|
+
notification_date_time: notification_obj.dig(:date_time),
|
26
|
+
type: types[top_level_key]
|
27
|
+
}.merge(message)
|
28
|
+
when :sms_subscription_cancellation_notification
|
29
|
+
{
|
30
|
+
subscription_id: id_from(notification_obj.dig(:link, 0, :href)),
|
31
|
+
notification_id: notification_obj.dig(:id),
|
32
|
+
notification_date_time: notification_obj.dig(:date_time),
|
33
|
+
type: types[top_level_key]
|
34
|
+
}
|
35
|
+
when :sms_event_notification
|
36
|
+
{
|
37
|
+
notification_id: notification_obj.dig(:id),
|
38
|
+
notification_date_time: notification_obj.dig(:date_time),
|
39
|
+
message_id: id_from(notification_obj.dig(:link, 0, :href)),
|
40
|
+
type: types[top_level_key],
|
41
|
+
event_details: {
|
42
|
+
description: notification_obj.dig(:event_description),
|
43
|
+
type: notification_obj.dig(:event_type)
|
44
|
+
}
|
45
|
+
}
|
46
|
+
else
|
47
|
+
notification_obj
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def self.types
|
54
|
+
{
|
55
|
+
outbound_sms_message_notification: 'outbound',
|
56
|
+
inbound_sms_message_notification: 'inbound',
|
57
|
+
sms_subscription_cancellation_notification: 'subscriptionCancel',
|
58
|
+
sms_event_notification: 'event'
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'cpaas-sdk/util'
|
2
|
+
|
3
|
+
module Cpaas
|
4
|
+
# @private
|
5
|
+
|
6
|
+
class NotificationChannel
|
7
|
+
def self.create_channel(params)
|
8
|
+
options = {
|
9
|
+
body: {
|
10
|
+
notificationChannel: {
|
11
|
+
channelData: {
|
12
|
+
'x-webhookURL': params[:webhook_url]
|
13
|
+
},
|
14
|
+
channelType: 'webhooks',
|
15
|
+
clientCorrelator: Cpaas.api.client_correlator
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
response = Cpaas.api.send_request("#{base_url}/channels", options, :post)
|
21
|
+
|
22
|
+
process_response(response) do |res|
|
23
|
+
channel = res.dig(:notification_channel)
|
24
|
+
|
25
|
+
{
|
26
|
+
channel_id: channel[:callback_url],
|
27
|
+
webhook_url: channel[:channel_data][:x_webhook_url],
|
28
|
+
channel_type: channel[:channel_type]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def self.base_url
|
36
|
+
"/cpaas/notificationchannel/v1/#{Cpaas.api.user_id}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'cpaas-sdk/util'
|
2
|
+
|
3
|
+
module Cpaas
|
4
|
+
##
|
5
|
+
# CPaaS provides Authentication API where a two-factor authentication (2FA) flow can be implemented by using that.
|
6
|
+
# Sections below describe two sample use cases, two-factor authentication via SMS and two-factor authentication via e-mail
|
7
|
+
#
|
8
|
+
|
9
|
+
class Twofactor
|
10
|
+
#
|
11
|
+
# Create a new authentication code.
|
12
|
+
#
|
13
|
+
# @param params [Hash]
|
14
|
+
# @option params [String|Array[string]] :destination_address Destination address of the authentication code being sent. For sms type authentication codes, it should contain a E164 phone number. For e-mail type authentication codes, it should contain a valid e-mail address.
|
15
|
+
# @option params [String] :message Message text sent to the destination, containing the placeholder for the code within the text. CPaaS requires to have *{code}* string within the text in order to generate a code and inject into the text. For email type code, one usage is to have the *{code}* string located within the link in order to get a unique link.
|
16
|
+
# @option params [String] :method ('sms') +optional+ Type of the authentication code delivery method, sms and email are supported types. Possible values: sms, email
|
17
|
+
# @option params [Number] :expiry (120) +optional+ Lifetime duration of the code sent in seconds. This can contain values between 30 and 3600 seconds.
|
18
|
+
# @option params [Number] :length (6) +optional+ Length of the authentication code tha CPaaS should generate for this request. It can contain values between 4 and 10.
|
19
|
+
# @option params [String] :type ('numeric') +optional+ Type of the code that is generated. If not provided, default value is numeric. Possible values: numeric, alphanumeric, alphabetic
|
20
|
+
#
|
21
|
+
def self.send_code(params = {})
|
22
|
+
address = (params[:destination_address].is_a? String) ? [ params[:destination_address] ] : params[:destination_address]
|
23
|
+
|
24
|
+
options = {
|
25
|
+
body: {
|
26
|
+
code: {
|
27
|
+
address: address,
|
28
|
+
method: params[:method] || 'sms',
|
29
|
+
format: {
|
30
|
+
length: params[:length] || 6,
|
31
|
+
type: params[:type] || 'numeric'
|
32
|
+
},
|
33
|
+
expiry: params[:expiry] || 120,
|
34
|
+
message: params[:message]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
response = Cpaas.api.send_request("#{base_url}/codes", options, :post)
|
40
|
+
|
41
|
+
process_response(response) do |res|
|
42
|
+
{
|
43
|
+
code_id: id_from(res.dig(:code, :resource_url))
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Verifying authentication code
|
50
|
+
#
|
51
|
+
# @param params [Hash]
|
52
|
+
# @option params [String] :code_id ID of the authentication code.
|
53
|
+
# @option params [String] :verification_code Code that is being verified
|
54
|
+
#
|
55
|
+
def self.verify_code(params = {})
|
56
|
+
options = {
|
57
|
+
body: {
|
58
|
+
code: {
|
59
|
+
verify: params[:verification_code]
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
response = Cpaas.api.send_request("#{base_url}/codes/#{params[:code_id]}/verify", options, :put)
|
65
|
+
|
66
|
+
process_response(response) do |res|
|
67
|
+
if res[:status_code] == 204
|
68
|
+
{
|
69
|
+
verified: true,
|
70
|
+
message: 'Success'
|
71
|
+
}
|
72
|
+
else
|
73
|
+
{
|
74
|
+
verified: false,
|
75
|
+
message: 'Code invalid or expired'
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Resending the authentication code via same code resource, invalidating the previously sent code.
|
83
|
+
#
|
84
|
+
# @param params [Hash]
|
85
|
+
# @option params [String|Array[string]] :destination_address Destination address of the authentication code being sent. For sms type authentication codes, it should contain a E164 phone number. For e-mail type authentication codes, it should contain a valid e-mail address.
|
86
|
+
# @option params [String] :message Message text sent to the destination, containing the placeholder for the code within the text. CPaaS requires to have *{code}* string within the text in order to generate a code and inject into the text. For email type code, one usage is to have the *{code}* string located within the link in order to get a unique link.
|
87
|
+
# @option params [String] :code_id ID of the authentication code.
|
88
|
+
# @option params [String] :method ('sms') +optional+ Type of the authentication code delivery method, sms and email are supported types. Possible values: sms, email
|
89
|
+
# @option params [Number] :expiry (120) +optional+ Lifetime duration of the code sent in seconds. This can contain values between 30 and 3600 seconds.
|
90
|
+
# @option params [Number] :length (6) +optional+ Length of the authentication code tha CPaaS should generate for this request. It can contain values between 4 and 10.
|
91
|
+
# @option params [String] :type ('numeric') +optional+ Type of the code that is generated. If not provided, default value is numeric. Possible values: numeric, alphanumeric, alphabetic
|
92
|
+
#
|
93
|
+
def self.resend_code(params = {})
|
94
|
+
address = (params[:destination_address].is_a? String) ? [ params[:destination_address] ] : params[:destination_address]
|
95
|
+
|
96
|
+
options = {
|
97
|
+
body: {
|
98
|
+
code: {
|
99
|
+
address: address,
|
100
|
+
method: params[:method] || 'sms',
|
101
|
+
format: {
|
102
|
+
length: params[:length] || 6,
|
103
|
+
type: params[:type] || 'numeric'
|
104
|
+
},
|
105
|
+
expiry: params[:expiry] || 120,
|
106
|
+
message: params[:message]
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
response = Cpaas.api.send_request("#{base_url}/codes/#{params[:code_id]}", options, :put)
|
112
|
+
|
113
|
+
process_response(response) do |res|
|
114
|
+
{
|
115
|
+
code_id: id_from(res.dig(:code, :resource_url))
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Delete authentication code resource.
|
122
|
+
#
|
123
|
+
# @param [Hash] params
|
124
|
+
# @option params [String] :code_id ID of the authentication code.
|
125
|
+
#
|
126
|
+
def self.delete_code(params = {})
|
127
|
+
Cpaas.api.send_request("#{base_url}/codes/#{params[:code_id]}", {}, :delete)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def self.base_url
|
133
|
+
"/cpaas/auth/v1/#{Cpaas.api.user_id}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
def process_response(res, remove_outer_key = true)
|
2
|
+
if res.key? :exception_id
|
3
|
+
response = res
|
4
|
+
elsif res && res.dig(:__for_test__)
|
5
|
+
custom_body = res.dig(:custom_body)
|
6
|
+
reject(res, :custom_body)
|
7
|
+
|
8
|
+
if !res.dig(:custom_body).nil? && block_given?
|
9
|
+
custom_body = yield res[:custom_body]
|
10
|
+
end
|
11
|
+
|
12
|
+
response = custom_body.nil? ? res : res.merge(custom_body)
|
13
|
+
elsif block_given?
|
14
|
+
response = yield res
|
15
|
+
elsif remove_outer_key
|
16
|
+
topLevelKey = res.keys.first
|
17
|
+
response = res[topLevelKey].as_json
|
18
|
+
else
|
19
|
+
response = res
|
20
|
+
end
|
21
|
+
|
22
|
+
response
|
23
|
+
end
|
24
|
+
|
25
|
+
def compose_error_from(err_response)
|
26
|
+
error_obj = deep_find(err_response, :message_id)
|
27
|
+
|
28
|
+
if (error_obj)
|
29
|
+
message = error_obj[:text]
|
30
|
+
|
31
|
+
error_obj[:variables].each_with_index { |variable, index| message.gsub!("%#{index + 1}", variable) }
|
32
|
+
|
33
|
+
response = {
|
34
|
+
name: error_obj[:name],
|
35
|
+
exception_id: error_obj[:message_id],
|
36
|
+
message: message
|
37
|
+
}
|
38
|
+
else
|
39
|
+
response = {
|
40
|
+
name: err_response.keys.first,
|
41
|
+
exception_id: 'Unknown',
|
42
|
+
message: err_response[:message]
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def id_from (url)
|
48
|
+
url.split('/').last
|
49
|
+
end
|
50
|
+
|
51
|
+
def deep_find(object, key, parentKey = '')
|
52
|
+
result = nil
|
53
|
+
|
54
|
+
if object.respond_to?(:key?) && object.key?(key)
|
55
|
+
object[:name] = parentKey
|
56
|
+
return object
|
57
|
+
elsif object.is_a? Enumerable
|
58
|
+
object.each do |k, v|
|
59
|
+
result = deep_find(v, key, k)
|
60
|
+
|
61
|
+
return result if !result.nil?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
return result
|
66
|
+
end
|
67
|
+
|
68
|
+
def convert_hash_keys(value)
|
69
|
+
case value
|
70
|
+
when Array
|
71
|
+
value.map { |v| convert_hash_keys(v) }
|
72
|
+
when Hash
|
73
|
+
Hash[value.map { |k, v| [underscore_key(k), convert_hash_keys(v)] }]
|
74
|
+
else
|
75
|
+
value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def underscore_key(k)
|
80
|
+
underscore(k.to_s).to_sym
|
81
|
+
end
|
82
|
+
|
83
|
+
def underscore(str)
|
84
|
+
str.gsub(/::/, '/').
|
85
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
86
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
87
|
+
tr("-", "_").
|
88
|
+
downcase
|
89
|
+
end
|
90
|
+
|
91
|
+
def reject(obj, key)
|
92
|
+
obj.reject { |k,v| k == key }
|
93
|
+
end
|
data/tutorials/2FA.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Two-Factor Authentication
|
2
|
+
$KANDY$ provides [Authentication API](/developer/references/ruby/1.0.0#twofactor-send-code) using which a two-factor authentication (2FA) flow can be implemented.
|
3
|
+
|
4
|
+
Sections below describe two sample use cases, two-factor authentication via SMS and two-factor authentication via e-mail.
|
5
|
+
|
6
|
+
## Two-Factor Authentication via SMS
|
7
|
+
The following diagram explains a sample use case and its logical flow for two-factor authentication via SMS:
|
8
|
+
|
9
|
+
![2FA via SMS flow](2fa-flow.png)
|
10
|
+
|
11
|
+
1. User opens the MyApp web page, enters credentials, and clicks login
|
12
|
+
2. MyApp web server initiates two-factor authentication via SMS flow, sends request to $KANDY$ to send validation code
|
13
|
+
3. User receives the code via SMS
|
14
|
+
4. User enters the code on MyApp web page
|
15
|
+
5. User submits the code
|
16
|
+
6. MyApp web server sends validation request to $KANDY$ with the code user entered and receives the result
|
17
|
+
7. MyApp web page takes the action based on the result
|
18
|
+
|
19
|
+
Your $KANDY$ admin can purchase SMS DID and assign to the MyApp project, so that the two-factor authentication SMS sent to app users always has the same originating number seen on the phone. Otherwise, the number seen on app users' phones may differ per transaction.
|
20
|
+
|
21
|
+
First, MyApp web server sends request to send a two-factor authentication code:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Cpaas::Twofactor.send_code({
|
25
|
+
destination_address: '+12292990344',
|
26
|
+
method: 'sms',
|
27
|
+
expiry: 360,
|
28
|
+
message: 'Your code is {code}',
|
29
|
+
length: 6,
|
30
|
+
type: 'alphanumeric'
|
31
|
+
})
|
32
|
+
```
|
33
|
+
The response contains `code_id` which is a unique ID needed for `verify_code`. The response object can look something like this:
|
34
|
+
```ruby
|
35
|
+
{
|
36
|
+
code_id: '1fc8907-d336-4707-ad3c'
|
37
|
+
}
|
38
|
+
```
|
39
|
+
|
40
|
+
Walking through the method parameters:
|
41
|
+
|
42
|
+
+ `destination_address` is required with a routable destination number in E.164 format, either with tel schema or not. For v1, only one address is supported.
|
43
|
+
+ `method` is mandatory and must have `sms` for verification via SMS flow.
|
44
|
+
+ `expiry` indicates the desired period of time in seconds that the code will be valid on $KANDY$. It is optional having default value as 120 seconds, while application can ask for values between 30 and 3600 seconds.
|
45
|
+
+ `message` is required with a `{code}` string within the text so that $KANDY$ can replace that with the real code generated, and send it as the SMS content.
|
46
|
+
+ `length` indicates the desired length of the code, default value is 6 and application can request for values between 4 and 10.
|
47
|
+
+ `type` indicates the desired type of the code from the set `numeric`, `alphanumeric`, `alphabetic`. Default is `numeric`.
|
48
|
+
|
49
|
+
The `code_id` in the `response` object should be used for verifying the user’s verification code or resending a new verification code to the user.
|
50
|
+
|
51
|
+
To verify the code, here is an example:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Cpaas::Twofactor.verify_code({
|
55
|
+
code_id: '1fc8907-d336-4707-ad3c',
|
56
|
+
verification_code: '123456'
|
57
|
+
})
|
58
|
+
```
|
59
|
+
In the response, `verified: true` means code is verified and removed from $KANDY$, while `verified: false` indicates the code is not valid.
|
60
|
+
|
61
|
+
A successful verification will have the following response:
|
62
|
+
```ruby
|
63
|
+
{
|
64
|
+
verified: true,
|
65
|
+
message: 'Success'
|
66
|
+
}
|
67
|
+
```
|
68
|
+
An invalid/failed verification will have the following response:
|
69
|
+
```ruby
|
70
|
+
{
|
71
|
+
verified: false,
|
72
|
+
message: 'Code expired or invalid'
|
73
|
+
}
|
74
|
+
```
|
75
|
+
|
76
|
+
## Two-Factor Authentication via E-mail
|
77
|
+
A similar flow with the SMS section above can be implemented for e-mail verification.
|
78
|
+
|
79
|
+
The following email code request example, requires insertion of the generated code within the link that the user is supposed to click and navigate to the application's web service:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
Cpaas::Twofactor.send_code({
|
83
|
+
destination_address: 'johndev@someemail.com',
|
84
|
+
method: 'email',
|
85
|
+
length: 10,
|
86
|
+
type: 'alphanumeric',
|
87
|
+
expiry: 3600,
|
88
|
+
message: 'Here is your code: {code}'
|
89
|
+
})
|
90
|
+
```
|
91
|
+
The response contains `code_id` which is a unique ID needed for `verify_code`. The response object can look something like this:
|
92
|
+
```ruby
|
93
|
+
{
|
94
|
+
code_id: '1fc8907-d336-4707-ad3c'
|
95
|
+
}
|
96
|
+
```
|
97
|
+
|
98
|
+
As can be seen within the example, `method` parameter has `email` value, while `destination_address` field includes a destination e-mail address. For v1, only plain text is supported.
|
99
|
+
|
100
|
+
Verification procedure for two-factor authentication via e-mail is same with two-factor authentication via SMS as described in previous section.
|
101
|
+
|
102
|
+
## Additional Operations
|
103
|
+
The `code` can be:
|
104
|
+
|
105
|
+
+ Resend using the same resource, which "invalidates" the previously sent code and triggers a new SMS or email containing a new code.
|
106
|
+
+ Deleted explicitly if desired (deletion operation does not block the previously started send operation)
|
107
|
+
|
108
|
+
## References
|
109
|
+
For all two factor authentication related method details, refer to [Two Factor Authentication](/developer/references/ruby/1.0.0#twofactor-send-code).
|