├── .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 | [![Dependency Status](https://david-dm.org/evilpacket/node-authy.png)](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 | ![authy api key in console](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/account-security-api-key.width-800.png) 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 | ``` --------------------------------------------------------------------------------