├── .gitignore
├── .jshintignore
├── LICENSE
├── README.md
├── index.js
├── package.json
├── tests
└── tests.js
└── verify-legacy-v1.md
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 | *.swp
10 |
11 | pids
12 | logs
13 | results
14 |
15 | node_modules
16 | npm-debug.log
17 | .idea
18 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Written and Copyright © 2012 by Adam Baldwin
2 | Released under the terms of the MIT License:
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | this software and associated documentation files (the "Software"), to deal in
6 | the Software without restriction, including without limitation the rights to
7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 | the Software, and to permit persons to whom the Software is furnished to do so,
9 | subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://david-dm.org/evilpacket/node-authy)
2 |
3 | # Node.js Client for Twilio Authy Two-Factor Authentication (2FA) API
4 |
5 | [Authy](https://www.twilio.com/authy) client for Node.js written by Adam Baldwin.
6 |
7 | Documentation for this Node.js usage of the Authy API lives in the [official Twilio documentation](https://www.twilio.com/docs/authy/api/).
8 |
9 | The Authy API supports multiple channels of 2FA:
10 | * One-time passwords via SMS and voice.
11 | * Soft token ([TOTP](https://www.twilio.com/docs/glossary/totp) via the Authy App)
12 | * Push authentication via the Authy App
13 |
14 | If you only need SMS and Voice support for one-time passwords, we recommend using the [Twilio Verify API](https://www.twilio.com/docs/verify/api) instead.
15 |
16 | [More on how to choose between Authy and Verify here.](https://www.twilio.com/docs/verify/authy-vs-verify)
17 |
18 | ### Authy Quickstart
19 |
20 | For a full tutorial, check out the Node.js Authy Quickstart in our docs:
21 | * [Node.js Authy Quickstart](https://www.twilio.com/docs/authy/quickstart/two-factor-authentication-nodejs)
22 |
23 | ## Authy Node.js Installation
24 |
25 | Install with [npm](https://www.npmjs.com/):
26 |
27 | $ npm install authy
28 |
29 | ## Usage
30 |
31 | To use the Authy client, require `Authy` and initialize it with your production API Key found in the [Twilio Console](https://www.twilio.com/console/authy/applications/):
32 |
33 | ```js
34 | var authy = require('authy')('APIKEY');
35 | ```
36 |
37 | 
38 |
39 | ## 2FA Workflow
40 |
41 | 1. [Create a user](https://www.twilio.com/docs/authy/api/users#enabling-new-user)
42 | 2. [Send a one-time password](https://www.twilio.com/docs/authy/api/one-time-passwords)
43 | 3. [Verify a one-time password](https://www.twilio.com/docs/authy/api/one-time-passwords#verify-a-one-time-password)
44 |
45 | **OR**
46 |
47 | 1. [Create a user](https://www.twilio.com/docs/authy/api/users#enabling-new-user)
48 | 2. [Send a push authentication](https://www.twilio.com/docs/authy/api/push-authentications)
49 | 3. [Check a push authentication status](https://www.twilio.com/docs/authy/api/push-authentications#check-approval-request-status)
50 |
51 |
52 | ## Phone Verification
53 |
54 | [Phone verification now lives in the Twilio API](https://www.twilio.com/docs/verify/api) and has [Node.js support through the official Twilio helper libraries](https://www.twilio.com/docs/libraries/node).
55 |
56 | [Legacy (V1) documentation here.](verify-legacy-v1.md) **Verify V1 is not recommended for new development. Please consider using [Verify V2](https://www.twilio.com/docs/verify/api)**.
57 |
58 | ## Contributing
59 |
60 | Install dependencies:
61 |
62 | npm install
63 |
64 | To run tests:
65 |
66 | npm test
67 |
68 |
69 | ### Contributors
70 |
71 | - [Daniel Barnes](https://github.com/DanielBarnes)
72 | - [Josh Staples](https://github.com/josh-authy)
73 | - [Christian Muertz](https://github.com/christian-muertz)
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var request = require('request');
2 | var querystring = require("querystring");
3 | var VERSION = "1.1"
4 |
5 | module.exports = function (api_key, api_url) {
6 | return new Authy(api_key, api_url);
7 | };
8 |
9 | function Authy(apiKey, api_url) {
10 | this.apiKey = apiKey;
11 | this.apiURL = api_url || "https://api.authy.com";
12 | }
13 |
14 | Authy.prototype.register_user = function (email, cellphone, country_code, send_sms_install_link, callback) {
15 | var country = "1";
16 | var send_install_link = true;
17 | if (arguments.length > 4) {
18 | country = country_code;
19 | send_install_link = send_sms_install_link;
20 | }
21 | else if (arguments.length == 4) {
22 | callback = send_sms_install_link;
23 | if (typeof country_code == "boolean") {
24 | send_install_link = country_code;
25 | }
26 | else {
27 | country = country_code;
28 | }
29 | }
30 | else {
31 | callback = country_code;
32 | }
33 |
34 | this._request("post", "/protected/json/users/new", {
35 | "user[email]": email,
36 | "user[cellphone]": cellphone,
37 | "user[country_code]": country
38 | },
39 | callback,
40 | {
41 | send_install_link_via_sms: send_install_link
42 | }
43 | );
44 | };
45 |
46 | Authy.prototype.delete_user = function (id, callback) {
47 | this._request("post", "/protected/json/users/delete/" + querystring.escape(id), {}, callback);
48 | };
49 |
50 | Authy.prototype.user_status = function (id, callback) {
51 | this._request("get", "/protected/json/users/" + querystring.escape(id) + "/status", {}, callback);
52 | };
53 |
54 | Authy.prototype.verify = function (id, token, force, callback) {
55 | var qs = {};
56 |
57 | if (arguments.length > 3) {
58 | qs.force = force;
59 | } else {
60 | callback = force;
61 | }
62 |
63 | cleanToken = String(token).replace(/\D/g, "").substring(0, 16)
64 |
65 | if (cleanToken === '' || cleanToken == null) {
66 | callback(new Error("argument 'token' cannot be empty, null, or undefined"));
67 | return;
68 | }
69 |
70 | // Overwrite the default body to check the response.
71 | check_body_callback = function(err, res) {
72 | if(!err && res.token != "is valid") {
73 | err = {
74 | message: "Unknown API response."
75 | }
76 | res = null
77 | }
78 | callback(err, res)
79 | }
80 | this._request("get", "/protected/json/verify/" + querystring.escape(cleanToken) + "/" + querystring.escape(id), {}, check_body_callback, qs);
81 | };
82 |
83 | Authy.prototype.request_sms = function (id, force, callback) {
84 | var qs = {};
85 |
86 | if (arguments.length > 2) {
87 | qs.force = force;
88 | } else {
89 | callback = force;
90 | }
91 |
92 | this._request("get", "/protected/json/sms/" + querystring.escape(id), {}, callback, qs);
93 | };
94 |
95 | Authy.prototype.request_call = function (id, force, callback) {
96 | var qs = {};
97 |
98 | if (arguments.length > 2) {
99 | qs.force = force;
100 | } else {
101 | callback = force;
102 | }
103 |
104 | this._request("get", "/protected/json/call/" + querystring.escape(id), {}, callback, qs);
105 | };
106 |
107 | Authy.prototype.phones = function() {
108 | self = this;
109 | return {
110 | verification_start: function(phone_number, country_code, params, callback) {
111 | if (arguments.length == 3) {
112 | callback = params;
113 | params = {}
114 | } else if (typeof params !== "object") {
115 | params = {via: params}
116 | }
117 |
118 | options = {
119 | phone_number: phone_number,
120 | country_code: country_code,
121 | via: params.via || "sms",
122 | locale: params.locale,
123 | custom_message: params.custom_message,
124 | code_length: params.code_length,
125 | custom_code: params.custom_code
126 | };
127 |
128 | options = Object.assign(options, params);
129 |
130 | self._request("post", "/protected/json/phones/verification/start", options, callback);
131 | },
132 |
133 | verification_check: function(phone_number, country_code, verification_code, callback) {
134 | options = {
135 | phone_number: phone_number,
136 | country_code: country_code,
137 | verification_code: verification_code
138 | };
139 | self._request("get", "/protected/json/phones/verification/check", options, callback);
140 | },
141 |
142 | verification_status: function(phone_number, country_code, callback) {
143 | options = {
144 | phone_number: phone_number,
145 | country_code: country_code,
146 | };
147 | self._request("get", "/protected/json/phones/verification/status", options, callback);
148 | },
149 |
150 | info: function(phone_number, country_code, callback) {
151 | options = {
152 | phone_number: phone_number,
153 | country_code: country_code
154 | };
155 | self._request("get", "/protected/json/phones/info", options, callback);
156 | }
157 | };
158 | };
159 |
160 | Authy.prototype.check_approval_status= function (uuid,callback){
161 | var url="/onetouch/json/approval_requests/"+uuid;
162 | this._request("get", url, {}, callback);
163 | };
164 |
165 | Authy.prototype.send_approval_request= function (id,user_payload,hidden_details,logos,callback){
166 | var url="/onetouch/json/users/"+querystring.escape(id)+"/approval_requests";
167 |
168 | var message_parameters = {
169 | "message": user_payload.message,
170 | "details": user_payload.details || {},
171 | "hidden_details": hidden_details || {}
172 | };
173 |
174 | // only add logos if provided
175 | if(logos){
176 | message_parameters['logos'] = logos;
177 | }
178 |
179 | // only add expiration time if provided
180 | if(user_payload.seconds_to_expire){
181 | message_parameters['seconds_to_expire'] = user_payload.seconds_to_expire;
182 | }
183 |
184 | this._request("post", "/onetouch/json/users/"+querystring.escape(id)+"/approval_requests", message_parameters, callback);
185 |
186 | };
187 |
188 | Authy.prototype._request = function(type, path, params, callback, qs) {
189 | qs = qs || {}
190 | qs['api_key'] = this.apiKey;
191 |
192 | user_agent = "AuthyNode/"+VERSION+" (node "+process.version+")"
193 | headers = {
194 | "User-Agent": user_agent
195 | }
196 |
197 | options = {
198 | url: this.apiURL + path,
199 | form: params,
200 | headers: headers,
201 | qs: qs,
202 | json: true,
203 | jar: false,
204 | strictSSL: true
205 | }
206 |
207 | var callback_check = function (err, res, body) {
208 | if (!err) {
209 | if(res.statusCode === 200) {
210 | callback(null, body);
211 | } else {
212 | callback(body);
213 | }
214 | } else {
215 | callback(err);
216 | }
217 | };
218 |
219 | switch(type) {
220 |
221 | case "post":
222 | request.post(options, callback_check);
223 | break;
224 |
225 | case "get":
226 | request.get(options, callback_check);
227 | break;
228 | }
229 | };
230 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authy",
3 | "version": "1.4.0",
4 | "description": "Authy.com API lib for node.js",
5 | "main": "index.js",
6 | "directories": {
7 | "test": "tests"
8 | },
9 | "scripts": {
10 | "test": "nodeunit tests/tests.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git://github.com/evilpacket/node-authy.git"
15 | },
16 | "keywords": [
17 | "authentication",
18 | "auth",
19 | "authy",
20 | "security",
21 | "two factor",
22 | "2 factor"
23 | ],
24 | "author": "Adam Baldwin ",
25 | "license": "MIT",
26 | "dependencies": {
27 | "request": "^2.87.0"
28 | },
29 | "devDependencies": {
30 | "nodeunit": "*"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/tests.js:
--------------------------------------------------------------------------------
1 | var apikey = "0cd08abec2e9b9641e40e9470a7fc336";
2 | var authy = require('../index')(apikey, 'http://sandbox-api.authy.com');
3 |
4 | var test_user = {email: 'baldwin@andyet.net', phone: '825-589-8570', country: '57'};
5 |
6 | /*
7 | * Nodeunit swallows uncaught exceptions--get them back!
8 | */
9 | process.on('uncaughtException', function (err) {
10 | console.error(err.stack);
11 | });
12 |
13 | /*
14 | * Register New User Tests
15 | */
16 | exports['Register New User - Without country code or SMS install link param'] = function (test) {
17 | authy.register_user(test_user.email, test_user.phone, function (err, res) {
18 | test.ok(res);
19 | test.equal(typeof(res), 'object', 'Response should be an object.');
20 | test.ok(res.user);
21 | test_user.id = res.user.id; // Save ID for future tests
22 | test.done();
23 | });
24 | };
25 |
26 | exports['Register New User - With country code but no SMS install link param'] = function (test) {
27 | authy.register_user(test_user.email, test_user.phone, test_user.country, function (err, res) {
28 | test.ok(res);
29 | test.equal(typeof(res), 'object', 'Response should be an object.');
30 | test.done();
31 | });
32 | };
33 |
34 | exports['Register New User - With country code and SMS install link param'] = function (test) {
35 | authy.register_user(test_user.email, test_user.phone, test_user.country, false, function (err, res) {
36 | test.ok(res);
37 | test.equal(typeof(res), 'object', 'Response should be an object.');
38 | test.done();
39 | });
40 | };
41 |
42 | exports['Register New User - With SMS install link param but no country code'] = function (test) {
43 | authy.register_user(test_user.email, test_user.phone, false, function (err, res) {
44 | test.ok(res);
45 | test.equal(typeof(res), 'object', 'Response should be an object.');
46 | test.done();
47 | });
48 | };
49 |
50 | exports['Register New User - Blank Email'] = function (test) {
51 | authy.register_user(null, test_user.phone, test_user.country, function (err, res) {
52 | test.ok(err, 'Should get error.');
53 | test.equal(typeof(err), 'object', 'Error should be an object.');
54 | test.equal(err.success, false, 'Success should be false.')
55 | test.done();
56 | });
57 | };
58 |
59 | exports['Register New User - Blank Phone'] = function (test) {
60 | authy.register_user(test_user.email, null, test_user.country, function (err, res) {
61 | test.ok(err, 'Should get error.');
62 | test.equal(typeof(err), 'object', 'Error should be an object.');
63 | test.equal(err.success, false, 'Success should be false.')
64 | test.done();
65 | });
66 | };
67 |
68 | exports['Register New User - Invalid Country Code'] = function (test) {
69 | authy.register_user(test_user.email, null, -100, function (err, res) {
70 | test.ok(err, 'Should get error.');
71 | test.equal(typeof(err), 'object', 'Error should be an object.');
72 | test.equal(err.success, false, 'Success should be false.')
73 | test.done();
74 | });
75 | };
76 |
77 | /*
78 | * Verify Token Tests
79 | */
80 | exports['Verify Token'] = function (test) {
81 | authy.verify(test_user.id, '0000000', function (err, res) {
82 | test.ok(res);
83 | test.equal(typeof(res), 'object', 'Response should be an object.');
84 | test.done();
85 | });
86 | };
87 |
88 | exports['Verify Token - Force'] = function (test) {
89 | authy.verify(test_user.id, '0000000', true, function (err, res) {
90 | test.ok(res);
91 | test.equal(typeof(res), 'object', 'Response should be an object.');
92 | test.done();
93 | });
94 | };
95 |
96 | exports['Verify Token - Invalid'] = function (test) {
97 | authy.verify(test_user.id, 'invalid', true, function (err, res) {
98 | test.ok(!res);
99 | test.equal(typeof(res), 'undefined', 'Response should be undefined.');
100 | test.done();
101 | });
102 | };
103 |
104 | exports['Verify Token - Dirty'] = function (test) {
105 | authy.verify(test_user.id, '0a0$0%0b0*@!#00', true, function (err, res) {
106 | test.ok(res);
107 | test.equal(typeof(res), 'object', 'Response should be an object.');
108 | test.done();
109 | });
110 | };
111 |
112 | exports['Verify Token - Empty String'] = function (test) {
113 | authy.verify(test_user.id, '', true, function (err, res) {
114 | test.ok(!res);
115 | test.equal(typeof(res), 'undefined', 'Response should be undefined.');
116 | test.ok(err instanceof Error);
117 | test.equal(err.toString(), "Error: argument 'token' cannot be empty, null, or undefined");
118 | test.done();
119 | });
120 | };
121 |
122 | exports['Verify Token - Null'] = function (test) {
123 | authy.verify(test_user.id, null, true, function (err, res) {
124 | test.ok(!res);
125 | test.equal(typeof(res), 'undefined', 'Response should be undefined.');
126 | test.ok(err instanceof Error);
127 | test.equal(err.toString(), "Error: argument 'token' cannot be empty, null, or undefined");
128 | test.done();
129 | });
130 | };
131 |
132 | exports['Verify Token - Undefined'] = function (test) {
133 | authy.verify(test_user.id, undefined, true, function (err, res) {
134 | test.ok(!res);
135 | test.equal(typeof(res), 'undefined', 'Response should be undefined.');
136 | test.ok(err instanceof Error);
137 | test.equal(err.toString(), "Error: argument 'token' cannot be empty, null, or undefined");
138 | test.done();
139 | });
140 | };
141 |
142 | /*
143 | * Request SMS Tests
144 | */
145 | exports['Request SMS'] = function (test) {
146 | authy.request_sms(test_user.id, function (err, res) {
147 | test.ok(res);
148 | test.done();
149 | });
150 | };
151 |
152 |
153 | exports['Request SMS - Force'] = function (test) {
154 | authy.request_sms(test_user.id, true, function (err, res) {
155 | test.ok(res);
156 | test.done();
157 | });
158 | };
159 |
160 | /*
161 | * Request Call Tests
162 | */
163 | exports['Request Call'] = function (test) {
164 | authy.request_call(test_user.id, function (err, res) {
165 | test.ok(res);
166 | test.done();
167 | });
168 | };
169 |
170 |
171 | exports['Request Call - Force'] = function (test) {
172 | authy.request_call(test_user.id, true, function (err, res) {
173 | test.ok(res);
174 | test.done();
175 | });
176 | };
177 |
178 | exports['OneTouch Create Request'] = function (test) {
179 | var user_payload = {'message': 'Your message here'};
180 | authy.send_approval_request(test_user.id, user_payload, {}, null, function (err, res) {
181 | test.ok(res);
182 | test.equal(typeof(res), 'object', 'Response should be an object.');
183 | test_user.uuid = res.approval_request.uuid;
184 | test.done();
185 | });
186 | };
187 |
188 | exports['OneTouch Check Approval Status'] = function (test) {
189 | var user_payload = {'message': 'Your message here'};
190 | authy.send_approval_request(test_user.uuid, function (err, res) {
191 | test.ok(res);
192 | test.equal(typeof(res), 'object', 'Response should be an object.');
193 | test.equal(res.approval_request.status, 'pending', 'Request should be pending');
194 | test.done();
195 | });
196 | };
197 |
198 | /*
199 | * Users tests
200 | */
201 | exports.users = {
202 | status: {
203 | setUp: function(callback) {
204 | authy.register_user(test_user.email, test_user.phone, function(err, res) {
205 | test_user.id = res.user.id;
206 | callback();
207 | });
208 | },
209 |
210 | does_not_exists: function(test) {
211 | authy.user_status("tony", function (err, res) {
212 | test.ok(err, 'Should get error.');
213 | test.equal(typeof(err), 'object', 'Error should be an object.');
214 | test.equal(err.success, false, 'Success should be false.')
215 | test.equal(err.message, "User not found.");
216 | test.done();
217 | });
218 | },
219 |
220 | exists: function(test) {
221 | authy.user_status(test_user.id, function (err, res) {
222 | test.ok(res);
223 | test.equal(typeof(res), 'object', 'res should be an object.');
224 | test.equal(res.success, true, 'Success should be true.')
225 | test.equal(typeof(res.status), 'object');
226 | test.done();
227 | });
228 | }
229 | }
230 | };
231 |
232 | /*
233 | * Phone Info tests
234 | */
235 | exports.phones = {
236 | verification_starts: {
237 | without_via: function (test) {
238 | test.expect(1);
239 | authy.phones().verification_start("111-111-1111", "1",
240 | function (err, res) {
241 | test.ok(res);
242 | test.done();
243 | }
244 | );
245 | },
246 |
247 | with_via: function (test) {
248 | test.expect(1);
249 | authy.phones().verification_start("111-111-1111", "1", "sms",
250 | function (err, res) {
251 | test.ok(res);
252 | test.done();
253 | }
254 | );
255 | },
256 |
257 | with_params: function (test) {
258 | test.expect(1);
259 | var params = {
260 | via: "sms",
261 | locale: "pl",
262 | custom_message: "This is a custom message",
263 | code_length: "6",
264 | custom_code: "12345"
265 | }
266 | authy.phones().verification_start("111-111-1111", "1", params,
267 | function (err, res) {
268 | test.ok(res);
269 | test.done();
270 | }
271 | );
272 | },
273 |
274 | with_params_locale_only: function (test) {
275 | test.expect(1);
276 | var params = {
277 | locale: "pl"
278 | }
279 | authy.phones().verification_start("111-111-1111", "1", params,
280 | function (err, res) {
281 | test.ok(res);
282 | test.done();
283 | }
284 | );
285 | }
286 | },
287 |
288 | verification_check: function (test) {
289 | test.expect(1);
290 | authy.phones().verification_check("111-111-1111", "1", "0000",
291 | function (err, res) {
292 | test.ok(res);
293 | test.done();
294 | }
295 | );
296 | },
297 |
298 | verification_status: function (test) {
299 | test.expect(1);
300 | authy.phones().verification_status("111-111-1111", "1",
301 | function (err, res) {
302 | test.ok(res);
303 | test.done();
304 | }
305 | );
306 | },
307 |
308 | info: function (test) {
309 | test.expect(1);
310 | authy.phones().info("7754615609", "1",
311 | function (err, res) {
312 | test.ok(res);
313 | test.done();
314 | }
315 | );
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/verify-legacy-v1.md:
--------------------------------------------------------------------------------
1 | # Phone Verification V1
2 |
3 | [Version 2 of the Verify API is now available!](https://www.twilio.com/docs/verify/api) V2 has an improved developer experience and new features. Some of the features of the V2 API include:
4 |
5 | * Twilio helper libraries in JavaScript, Java, C#, Python, Ruby, and PHP
6 | * PSD2 Secure Customer Authentication Support
7 | * Improved Visibility and Insights
8 |
9 | You are currently viewing Version 1. V1 of the API will be maintained for the time being, but any new features and development will be on Version 2. We strongly encourage you to do any new development with API V2. Check out the migration guide or the API Reference for more information.
10 |
11 | ### API Reference
12 |
13 | API Reference is available at https://www.twilio.com/docs/verify/api/v1
14 |
15 | #### Start Phone Verification
16 | Browse the [API docs](https://www.twilio.com/docs/verify/api/verification) for all available params.
17 |
18 | phones().verification_start(phone_number, country_code, params, callback);
19 |
20 | ```javascript
21 | authy.phones().verification_start('111-111-1111', '1', { via: 'sms', locale: 'en', code_length: '6' }, function(err, res) {
22 |
23 | });
24 | ```
25 |
26 | The `params` argument is optional and sets 'sms' as the default `via`, leaving the other two options blank.
27 |
28 |
29 | #### Check Phone Verification
30 | Browse the [API docs](https://www.twilio.com/docs/verify/api/verification) for all available params.
31 |
32 | phones().verification_check(phone_number, country_code, verification_code, callback);
33 |
34 | ```javascript
35 | authy.phones().verification_check('111-111-1111', '1', '0000', function (err, res) {
36 |
37 | });
38 | ```
39 |
40 | #### Status of Phone Verification
41 | Browse the [API docs](https://www.twilio.com/docs/verify/api/verification) for all available params.
42 |
43 | phones().verification_status(phone_number, country_code, callback);
44 |
45 | ```javascript
46 | authy.phones().verification_status('111-111-1111', '1', function (err, res) {
47 |
48 | });
49 | ```
--------------------------------------------------------------------------------