├── env.example ├── index.js ├── package-lock.json ├── package.json ├── readme.md └── views ├── layouts └── main.handlebars ├── step1.handlebars ├── step2.handlebars └── step3.handlebars /env.example: -------------------------------------------------------------------------------- 1 | MESSAGEBIRD_API_KEY=YOUR-API-KEY -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Load Dependencies 2 | var express = require('express'); 3 | var exphbs = require('express-handlebars'); 4 | var bodyParser = require('body-parser'); 5 | 6 | // Load configuration from .env file 7 | require('dotenv').config(); 8 | 9 | // Load and initialize MesageBird SDK 10 | var messagebird = require('messagebird')(process.env.MESSAGEBIRD_API_KEY); 11 | 12 | // Set up and configure the Express framework 13 | var app = express(); 14 | app.engine('handlebars', exphbs({defaultLayout: 'main'})); 15 | app.set('view engine', 'handlebars'); 16 | app.use(bodyParser.urlencoded({ extended : true })); 17 | 18 | // Display page to ask the user for their phone number 19 | app.get('/', function(req, res) { 20 | res.render('step1'); 21 | }); 22 | 23 | // Handle phone number submission 24 | app.post('/step2', function(req, res) { 25 | var number = req.body.number; 26 | 27 | // Make request to Verify API 28 | messagebird.verify.create(number, { 29 | originator : 'Code', 30 | template : 'Your verification code is %token.' 31 | }, function (err, response) { 32 | if (err) { 33 | // Request has failed 34 | console.log(err); 35 | res.render('step1', { 36 | error : err.errors[0].description 37 | }); 38 | } else { 39 | // Request was successful 40 | console.log(response); 41 | res.render('step2', { 42 | id : response.id 43 | }); 44 | } 45 | }) 46 | }); 47 | 48 | // Verify whether the token is correct 49 | app.post('/step3', function(req, res) { 50 | var id = req.body.id; 51 | var token = req.body.token; 52 | 53 | // Make request to Verify API 54 | messagebird.verify.verify(id, token, function(err, response) { 55 | if (err) { 56 | // Verification has failed 57 | console.log(err); 58 | res.render('step2', { 59 | error: err.errors[0].description, 60 | id : id 61 | }); 62 | } else { 63 | // Verification was successful 64 | console.log(response); 65 | res.render('step3'); 66 | } 67 | }) 68 | }); 69 | 70 | // Start the application 71 | app.listen(8080); -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-messagebird-verify-example", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "accepts": { 7 | "version": "1.3.5", 8 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 9 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 10 | "requires": { 11 | "mime-types": "~2.1.18", 12 | "negotiator": "0.6.1" 13 | } 14 | }, 15 | "align-text": { 16 | "version": "0.1.4", 17 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 18 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 19 | "requires": { 20 | "kind-of": "^3.0.2", 21 | "longest": "^1.0.1", 22 | "repeat-string": "^1.5.2" 23 | } 24 | }, 25 | "amdefine": { 26 | "version": "1.0.1", 27 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 28 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" 29 | }, 30 | "array-flatten": { 31 | "version": "1.1.1", 32 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 33 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 34 | }, 35 | "asap": { 36 | "version": "2.0.6", 37 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 38 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 39 | }, 40 | "async": { 41 | "version": "1.5.2", 42 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 43 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" 44 | }, 45 | "balanced-match": { 46 | "version": "1.0.0", 47 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 48 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 49 | }, 50 | "body-parser": { 51 | "version": "1.18.3", 52 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 53 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 54 | "requires": { 55 | "bytes": "3.0.0", 56 | "content-type": "~1.0.4", 57 | "debug": "2.6.9", 58 | "depd": "~1.1.2", 59 | "http-errors": "~1.6.3", 60 | "iconv-lite": "0.4.23", 61 | "on-finished": "~2.3.0", 62 | "qs": "6.5.2", 63 | "raw-body": "2.3.3", 64 | "type-is": "~1.6.16" 65 | } 66 | }, 67 | "brace-expansion": { 68 | "version": "1.1.11", 69 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 70 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 71 | "requires": { 72 | "balanced-match": "^1.0.0", 73 | "concat-map": "0.0.1" 74 | } 75 | }, 76 | "bytes": { 77 | "version": "3.0.0", 78 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 79 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 80 | }, 81 | "camelcase": { 82 | "version": "1.2.1", 83 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 84 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 85 | "optional": true 86 | }, 87 | "center-align": { 88 | "version": "0.1.3", 89 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 90 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 91 | "optional": true, 92 | "requires": { 93 | "align-text": "^0.1.3", 94 | "lazy-cache": "^1.0.3" 95 | } 96 | }, 97 | "cliui": { 98 | "version": "2.1.0", 99 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 100 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 101 | "optional": true, 102 | "requires": { 103 | "center-align": "^0.1.1", 104 | "right-align": "^0.1.1", 105 | "wordwrap": "0.0.2" 106 | }, 107 | "dependencies": { 108 | "wordwrap": { 109 | "version": "0.0.2", 110 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 111 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 112 | "optional": true 113 | } 114 | } 115 | }, 116 | "concat-map": { 117 | "version": "0.0.1", 118 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 119 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 120 | }, 121 | "content-disposition": { 122 | "version": "0.5.2", 123 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 124 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 125 | }, 126 | "content-type": { 127 | "version": "1.0.4", 128 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 129 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 130 | }, 131 | "cookie": { 132 | "version": "0.3.1", 133 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 134 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 135 | }, 136 | "cookie-signature": { 137 | "version": "1.0.6", 138 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 139 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 140 | }, 141 | "debug": { 142 | "version": "2.6.9", 143 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 144 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 145 | "requires": { 146 | "ms": "2.0.0" 147 | } 148 | }, 149 | "decamelize": { 150 | "version": "1.2.0", 151 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 152 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 153 | "optional": true 154 | }, 155 | "define-properties": { 156 | "version": "1.1.2", 157 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", 158 | "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", 159 | "requires": { 160 | "foreach": "^2.0.5", 161 | "object-keys": "^1.0.8" 162 | } 163 | }, 164 | "depd": { 165 | "version": "1.1.2", 166 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 167 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 168 | }, 169 | "destroy": { 170 | "version": "1.0.4", 171 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 172 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 173 | }, 174 | "dotenv": { 175 | "version": "5.0.1", 176 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", 177 | "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" 178 | }, 179 | "ee-first": { 180 | "version": "1.1.1", 181 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 182 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 183 | }, 184 | "encodeurl": { 185 | "version": "1.0.2", 186 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 187 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 188 | }, 189 | "escape-html": { 190 | "version": "1.0.3", 191 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 192 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 193 | }, 194 | "etag": { 195 | "version": "1.8.1", 196 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 197 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 198 | }, 199 | "express": { 200 | "version": "4.16.3", 201 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 202 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 203 | "requires": { 204 | "accepts": "~1.3.5", 205 | "array-flatten": "1.1.1", 206 | "body-parser": "1.18.2", 207 | "content-disposition": "0.5.2", 208 | "content-type": "~1.0.4", 209 | "cookie": "0.3.1", 210 | "cookie-signature": "1.0.6", 211 | "debug": "2.6.9", 212 | "depd": "~1.1.2", 213 | "encodeurl": "~1.0.2", 214 | "escape-html": "~1.0.3", 215 | "etag": "~1.8.1", 216 | "finalhandler": "1.1.1", 217 | "fresh": "0.5.2", 218 | "merge-descriptors": "1.0.1", 219 | "methods": "~1.1.2", 220 | "on-finished": "~2.3.0", 221 | "parseurl": "~1.3.2", 222 | "path-to-regexp": "0.1.7", 223 | "proxy-addr": "~2.0.3", 224 | "qs": "6.5.1", 225 | "range-parser": "~1.2.0", 226 | "safe-buffer": "5.1.1", 227 | "send": "0.16.2", 228 | "serve-static": "1.13.2", 229 | "setprototypeof": "1.1.0", 230 | "statuses": "~1.4.0", 231 | "type-is": "~1.6.16", 232 | "utils-merge": "1.0.1", 233 | "vary": "~1.1.2" 234 | }, 235 | "dependencies": { 236 | "body-parser": { 237 | "version": "1.18.2", 238 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 239 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 240 | "requires": { 241 | "bytes": "3.0.0", 242 | "content-type": "~1.0.4", 243 | "debug": "2.6.9", 244 | "depd": "~1.1.1", 245 | "http-errors": "~1.6.2", 246 | "iconv-lite": "0.4.19", 247 | "on-finished": "~2.3.0", 248 | "qs": "6.5.1", 249 | "raw-body": "2.3.2", 250 | "type-is": "~1.6.15" 251 | } 252 | }, 253 | "iconv-lite": { 254 | "version": "0.4.19", 255 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 256 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 257 | }, 258 | "qs": { 259 | "version": "6.5.1", 260 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 261 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 262 | }, 263 | "raw-body": { 264 | "version": "2.3.2", 265 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 266 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 267 | "requires": { 268 | "bytes": "3.0.0", 269 | "http-errors": "1.6.2", 270 | "iconv-lite": "0.4.19", 271 | "unpipe": "1.0.0" 272 | }, 273 | "dependencies": { 274 | "depd": { 275 | "version": "1.1.1", 276 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 277 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 278 | }, 279 | "http-errors": { 280 | "version": "1.6.2", 281 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 282 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 283 | "requires": { 284 | "depd": "1.1.1", 285 | "inherits": "2.0.3", 286 | "setprototypeof": "1.0.3", 287 | "statuses": ">= 1.3.1 < 2" 288 | } 289 | }, 290 | "setprototypeof": { 291 | "version": "1.0.3", 292 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 293 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 294 | } 295 | } 296 | }, 297 | "statuses": { 298 | "version": "1.4.0", 299 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 300 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 301 | } 302 | } 303 | }, 304 | "express-handlebars": { 305 | "version": "3.0.0", 306 | "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.0.tgz", 307 | "integrity": "sha1-gKBwu4GbCeSvLKbQeA91zgXnXC8=", 308 | "requires": { 309 | "glob": "^6.0.4", 310 | "graceful-fs": "^4.1.2", 311 | "handlebars": "^4.0.5", 312 | "object.assign": "^4.0.3", 313 | "promise": "^7.0.0" 314 | } 315 | }, 316 | "finalhandler": { 317 | "version": "1.1.1", 318 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 319 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 320 | "requires": { 321 | "debug": "2.6.9", 322 | "encodeurl": "~1.0.2", 323 | "escape-html": "~1.0.3", 324 | "on-finished": "~2.3.0", 325 | "parseurl": "~1.3.2", 326 | "statuses": "~1.4.0", 327 | "unpipe": "~1.0.0" 328 | }, 329 | "dependencies": { 330 | "statuses": { 331 | "version": "1.4.0", 332 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 333 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 334 | } 335 | } 336 | }, 337 | "foreach": { 338 | "version": "2.0.5", 339 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 340 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" 341 | }, 342 | "forwarded": { 343 | "version": "0.1.2", 344 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 345 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 346 | }, 347 | "fresh": { 348 | "version": "0.5.2", 349 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 350 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 351 | }, 352 | "function-bind": { 353 | "version": "1.1.1", 354 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 355 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 356 | }, 357 | "glob": { 358 | "version": "6.0.4", 359 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 360 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 361 | "requires": { 362 | "inflight": "^1.0.4", 363 | "inherits": "2", 364 | "minimatch": "2 || 3", 365 | "once": "^1.3.0", 366 | "path-is-absolute": "^1.0.0" 367 | } 368 | }, 369 | "graceful-fs": { 370 | "version": "4.1.11", 371 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 372 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 373 | }, 374 | "handlebars": { 375 | "version": "4.0.11", 376 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", 377 | "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", 378 | "requires": { 379 | "async": "^1.4.0", 380 | "optimist": "^0.6.1", 381 | "source-map": "^0.4.4", 382 | "uglify-js": "^2.6" 383 | } 384 | }, 385 | "has-symbols": { 386 | "version": "1.0.0", 387 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 388 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" 389 | }, 390 | "http-errors": { 391 | "version": "1.6.3", 392 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 393 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 394 | "requires": { 395 | "depd": "~1.1.2", 396 | "inherits": "2.0.3", 397 | "setprototypeof": "1.1.0", 398 | "statuses": ">= 1.4.0 < 2" 399 | } 400 | }, 401 | "iconv-lite": { 402 | "version": "0.4.23", 403 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 404 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 405 | "requires": { 406 | "safer-buffer": ">= 2.1.2 < 3" 407 | } 408 | }, 409 | "inflight": { 410 | "version": "1.0.6", 411 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 412 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 413 | "requires": { 414 | "once": "^1.3.0", 415 | "wrappy": "1" 416 | } 417 | }, 418 | "inherits": { 419 | "version": "2.0.3", 420 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 421 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 422 | }, 423 | "ipaddr.js": { 424 | "version": "1.8.0", 425 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 426 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 427 | }, 428 | "is-buffer": { 429 | "version": "1.1.6", 430 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 431 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 432 | }, 433 | "kind-of": { 434 | "version": "3.2.2", 435 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 436 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 437 | "requires": { 438 | "is-buffer": "^1.1.5" 439 | } 440 | }, 441 | "lazy-cache": { 442 | "version": "1.0.4", 443 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 444 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 445 | "optional": true 446 | }, 447 | "longest": { 448 | "version": "1.0.1", 449 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 450 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" 451 | }, 452 | "media-typer": { 453 | "version": "0.3.0", 454 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 455 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 456 | }, 457 | "merge-descriptors": { 458 | "version": "1.0.1", 459 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 460 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 461 | }, 462 | "messagebird": { 463 | "version": "2.1.4", 464 | "resolved": "https://registry.npmjs.org/messagebird/-/messagebird-2.1.4.tgz", 465 | "integrity": "sha512-L/ytDaJalxix3Z9lZl9qto3qiEQuf8eC8nJpWDquR6pOLi9zS27Ij/zHFt0XnNeR6oHOXlKLyU4/2CrjG8gt5Q==" 466 | }, 467 | "methods": { 468 | "version": "1.1.2", 469 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 470 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 471 | }, 472 | "mime": { 473 | "version": "1.4.1", 474 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 475 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 476 | }, 477 | "mime-db": { 478 | "version": "1.35.0", 479 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", 480 | "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" 481 | }, 482 | "mime-types": { 483 | "version": "2.1.19", 484 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", 485 | "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", 486 | "requires": { 487 | "mime-db": "~1.35.0" 488 | } 489 | }, 490 | "minimatch": { 491 | "version": "3.0.4", 492 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 493 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 494 | "requires": { 495 | "brace-expansion": "^1.1.7" 496 | } 497 | }, 498 | "minimist": { 499 | "version": "0.0.10", 500 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 501 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" 502 | }, 503 | "ms": { 504 | "version": "2.0.0", 505 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 506 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 507 | }, 508 | "negotiator": { 509 | "version": "0.6.1", 510 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 511 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 512 | }, 513 | "object-keys": { 514 | "version": "1.0.11", 515 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", 516 | "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" 517 | }, 518 | "object.assign": { 519 | "version": "4.1.0", 520 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 521 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 522 | "requires": { 523 | "define-properties": "^1.1.2", 524 | "function-bind": "^1.1.1", 525 | "has-symbols": "^1.0.0", 526 | "object-keys": "^1.0.11" 527 | } 528 | }, 529 | "on-finished": { 530 | "version": "2.3.0", 531 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 532 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 533 | "requires": { 534 | "ee-first": "1.1.1" 535 | } 536 | }, 537 | "once": { 538 | "version": "1.4.0", 539 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 540 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 541 | "requires": { 542 | "wrappy": "1" 543 | } 544 | }, 545 | "optimist": { 546 | "version": "0.6.1", 547 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 548 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 549 | "requires": { 550 | "minimist": "~0.0.1", 551 | "wordwrap": "~0.0.2" 552 | } 553 | }, 554 | "parseurl": { 555 | "version": "1.3.2", 556 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 557 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 558 | }, 559 | "path-is-absolute": { 560 | "version": "1.0.1", 561 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 562 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 563 | }, 564 | "path-to-regexp": { 565 | "version": "0.1.7", 566 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 567 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 568 | }, 569 | "promise": { 570 | "version": "7.3.1", 571 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", 572 | "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", 573 | "requires": { 574 | "asap": "~2.0.3" 575 | } 576 | }, 577 | "proxy-addr": { 578 | "version": "2.0.4", 579 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 580 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 581 | "requires": { 582 | "forwarded": "~0.1.2", 583 | "ipaddr.js": "1.8.0" 584 | } 585 | }, 586 | "qs": { 587 | "version": "6.5.2", 588 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 589 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 590 | }, 591 | "range-parser": { 592 | "version": "1.2.0", 593 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 594 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 595 | }, 596 | "raw-body": { 597 | "version": "2.3.3", 598 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 599 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 600 | "requires": { 601 | "bytes": "3.0.0", 602 | "http-errors": "1.6.3", 603 | "iconv-lite": "0.4.23", 604 | "unpipe": "1.0.0" 605 | } 606 | }, 607 | "repeat-string": { 608 | "version": "1.6.1", 609 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 610 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" 611 | }, 612 | "right-align": { 613 | "version": "0.1.3", 614 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 615 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 616 | "optional": true, 617 | "requires": { 618 | "align-text": "^0.1.1" 619 | } 620 | }, 621 | "safe-buffer": { 622 | "version": "5.1.1", 623 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 624 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 625 | }, 626 | "safer-buffer": { 627 | "version": "2.1.2", 628 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 629 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 630 | }, 631 | "send": { 632 | "version": "0.16.2", 633 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 634 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 635 | "requires": { 636 | "debug": "2.6.9", 637 | "depd": "~1.1.2", 638 | "destroy": "~1.0.4", 639 | "encodeurl": "~1.0.2", 640 | "escape-html": "~1.0.3", 641 | "etag": "~1.8.1", 642 | "fresh": "0.5.2", 643 | "http-errors": "~1.6.2", 644 | "mime": "1.4.1", 645 | "ms": "2.0.0", 646 | "on-finished": "~2.3.0", 647 | "range-parser": "~1.2.0", 648 | "statuses": "~1.4.0" 649 | }, 650 | "dependencies": { 651 | "statuses": { 652 | "version": "1.4.0", 653 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 654 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 655 | } 656 | } 657 | }, 658 | "serve-static": { 659 | "version": "1.13.2", 660 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 661 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 662 | "requires": { 663 | "encodeurl": "~1.0.2", 664 | "escape-html": "~1.0.3", 665 | "parseurl": "~1.3.2", 666 | "send": "0.16.2" 667 | } 668 | }, 669 | "setprototypeof": { 670 | "version": "1.1.0", 671 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 672 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 673 | }, 674 | "source-map": { 675 | "version": "0.4.4", 676 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", 677 | "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", 678 | "requires": { 679 | "amdefine": ">=0.0.4" 680 | } 681 | }, 682 | "statuses": { 683 | "version": "1.5.0", 684 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 685 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 686 | }, 687 | "type-is": { 688 | "version": "1.6.16", 689 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 690 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 691 | "requires": { 692 | "media-typer": "0.3.0", 693 | "mime-types": "~2.1.18" 694 | } 695 | }, 696 | "uglify-js": { 697 | "version": "2.8.29", 698 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 699 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 700 | "optional": true, 701 | "requires": { 702 | "source-map": "~0.5.1", 703 | "uglify-to-browserify": "~1.0.0", 704 | "yargs": "~3.10.0" 705 | }, 706 | "dependencies": { 707 | "source-map": { 708 | "version": "0.5.7", 709 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 710 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 711 | "optional": true 712 | } 713 | } 714 | }, 715 | "uglify-to-browserify": { 716 | "version": "1.0.2", 717 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 718 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 719 | "optional": true 720 | }, 721 | "unpipe": { 722 | "version": "1.0.0", 723 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 724 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 725 | }, 726 | "utils-merge": { 727 | "version": "1.0.1", 728 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 729 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 730 | }, 731 | "vary": { 732 | "version": "1.1.2", 733 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 734 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 735 | }, 736 | "window-size": { 737 | "version": "0.1.0", 738 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 739 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 740 | "optional": true 741 | }, 742 | "wordwrap": { 743 | "version": "0.0.3", 744 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 745 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" 746 | }, 747 | "wrappy": { 748 | "version": "1.0.2", 749 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 750 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 751 | }, 752 | "yargs": { 753 | "version": "3.10.0", 754 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 755 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 756 | "optional": true, 757 | "requires": { 758 | "camelcase": "^1.0.2", 759 | "cliui": "^2.1.0", 760 | "decamelize": "^1.0.0", 761 | "window-size": "0.1.0" 762 | } 763 | } 764 | } 765 | } 766 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-messagebird-verify-example", 3 | "main": "index.js", 4 | "dependencies": { 5 | "body-parser": "^1.18.3", 6 | "dotenv": "^5.0.1", 7 | "express": "^4.16.3", 8 | "express-handlebars": "^3.0.0", 9 | "messagebird": "^2.1.4" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Two Factor Authentication (2FA) 2 | ### ⏱ 15 min build time 3 | 4 | ## Why build two factor authentication? 5 | 6 | Enterprises are increasingly challenged to keep sensitive information from falling into the wrong hands. This means that we can no longer trust old online authentication systems that rely solely on usernames and passwords, especially as security breaches grow in frequency, severity and sophistication. 7 | 8 | With the [MessageBird Verify API](https://www.messagebird.com/en/verify), you can implement two factor authentication (2FA) solutions to provide an additional layer of account security by verifying the user's password with a second authentication token and in turn, secure customer data, block fraudulent accounts, and safeguard key transactions in a matter of minutes. The most common use case involves the application of one-time passwords (OTP) generated by hardware tokens or authenticator apps or directly sent to the user's mobile phone via SMS text messaging. 9 | 10 | In this MessageBird Developer Guide, we'll introduce the [MessageBird Verify API](https://www.messagebird.com/en/verify) and show you how to build a runnable application in Node.js. The application is a prototype for a two factor authentication system deployed by our fictitious online banking application called *BirdBank*. 11 | 12 | We'll walk you through the following steps: 13 | 14 | - Asking for the phone number 15 | - Sending a verification code 16 | - Verifying the code 17 | 18 | 19 | **Pro-tip:** Follow this tutorial to build the whole application from scratch or, if you want to see it in action right away, you can download, clone or fork the sample application from the [MessageBird Developer Guides GitHub repository](https://github.com/messagebirdguides/verify-guide). 20 | 21 | ## Getting started 22 | 23 | We'll use [Node.js](https://nodejs.org/en/), the [Express framework](https://github.com/expressjs/express) and the [Handlebars templating engine](https://www.npmjs.com/package/express-handlebars) as well as the [MessageBird SDK](https://www.npmjs.com/package/messagebird) to build our sample application. 24 | 25 | Before we get started, let's make sure Node's package manager - npm - is installed. If not, you can easily download it [here](https://www.npmjs.com/get-npm) for free. 26 | 27 | ## Project Setup 28 | 29 | ### Dependencies 30 | 31 | First, let's create a new directory to store the sample application. Now within this new directory, we'll create a file called `package.json` with the following content: 32 | 33 | ````json 34 | { 35 | "name": "node-messagebird-verify-example", 36 | "main": "index.js", 37 | "dependencies": { 38 | "body-parser": "^1.18.3", 39 | "dotenv": "^5.0.1", 40 | "express": "^4.16.3", 41 | "express-handlebars": "^3.0.0", 42 | "messagebird": "^2.1.4" 43 | } 44 | } 45 | ```` 46 | 47 | This file provides a **name** for your sample application, declares the **main**, which we'll create next, and lists the **dependencies** you'll need along with their versions. Apart from Express, Handlebars, and the MessageBird SDK, we're adding a small helper library called [body-parser](https://www.npmjs.com/package/body-parser) to help simplify your code. 48 | 49 | Next, we need to instruct npm to install all the required modules in your project. Let's open a console pointed to the directory that contains the file you just created and type the following command: 50 | 51 | ````bash 52 | npm install 53 | ```` 54 | 55 | #### Create your API Key 🔑 56 | 57 | To enable the MessageBird SDK, we need to provide an access key for the API. MessageBird provides keys in _live_ and _test_ modes. To get this application running, we will need to create and use a live API access key. Read more about the difference between test and live API keys [here] (https://support.messagebird.com/hc/en-us/articles/360000670709-What-is-the-difference-between-a-live-key-and-a-test-key-). 58 | 59 | Let's create your live API access key. First, go to the [MessageBird Dashboard](https://dashboard.messagebird.com/en/user/index); if you have already created an API key it will be shown right there. If you do not see any key on the dashboard or if you're unsure whether this key is in _live_ mode, go to the _Developers_ section and open the [API access (REST) tab](https://dashboard.messagebird.com/en/developers/access). Here, you can create new API keys and manage your existing ones. 60 | 61 | If you are having any issues creating your API key, please reach out to our Customer Support team at support@messagebird.com. 62 | 63 | **Pro-tip:** Hardcoding your credentials is a risky practice that should never be used in production applications. A better method, also recommended by the [Twelve-Factor App Definition](https://12factor.net/), is to use environment variables. 64 | 65 | We've added [dotenv](https://www.npmjs.com/package/dotenv) to the sample application, so you can supply your API key in a file named `.env`. You can copy the provided file `env.example` to `.env` and add your API key like this: 66 | 67 | ````env 68 | MESSAGEBIRD_API_KEY=YOUR-API-KEY 69 | ```` 70 | 71 | ### Main file 72 | 73 | You now have your API key, so let's get started with the main file. First, we create an `index.js` file in the same directory as your `package.json`. This file starts off by including the dependencies: 74 | 75 | ````javascript 76 | var express = require('express'); 77 | var exphbs = require('express-handlebars'); 78 | var bodyParser = require('body-parser'); 79 | ```` 80 | 81 | Then, we initialize dotenv to load the API key from the `.env` file: 82 | 83 | ````javascript 84 | require('dotenv').config(); 85 | ```` 86 | 87 | Next, append the following line to your `index.js` to include and set up the SDK: 88 | 89 | ````javascript 90 | var messagebird = require('messagebird')(process.env.MESSAGEBIRD_API_KEY); 91 | ```` 92 | 93 | Now, let's initialize and configure the framework and enable Handlebars and the body parser: 94 | 95 | ````javascript 96 | var app = express(); 97 | app.engine('handlebars', exphbs({defaultLayout: 'main'})); 98 | app.set('view engine', 'handlebars'); 99 | app.use(bodyParser.urlencoded({ extended : true })); 100 | ```` 101 | 102 | ### Views 103 | 104 | We use Handlebars to separate the logic of our code from the HTML pages. To do this, we create a directory named `views`. Inside `views` we create another directory named `layouts` and a file called `main.handlebars` inside with the following content: 105 | 106 | ````html 107 | 108 | 109 | 110 | MessageBird Verify Example 111 | 112 | 113 |

MessageBird Verify Example

114 | 115 | {{{body}}} 116 | 117 | 118 | ```` 119 | 120 | This is the main layout which acts as a container for all pages of our application. We'll create the views for each page next. 121 | 122 | ## Asking for the phone number 123 | 124 | The first step in verifying a user's phone number is asking them to provide their phone number. Let's do exactly this by creating an HTML form and storing it as `step1.handlebars` inside the `views` directory: 125 | 126 | ````html 127 | {{#if error}} 128 |

{{error}}

129 | {{/if}} 130 |

Please enter your phone number (in international format, starting with +) to receive a verification code:

131 |
132 | 133 | 134 |
135 | ```` 136 | 137 | The form is simple, having just one input field and one submit button. Providing `tel` as the `type` attribute of our input field allows some browsers, especially on mobile devices, to optimize for telephone number input, for example by displaying a numberpad-style keyboard. The section starting with `{{#if error}` is needed to display errors. We'll come back to this in a minute. 138 | 139 | Now, it's time to add a route to your `index.js` to display the page: 140 | 141 | ````javascript 142 | app.get('/', function(req, res) { 143 | res.render('step1'); 144 | }); 145 | ```` 146 | 147 | ## Sending a verification code 148 | 149 | Once we've collected the number, we can send a verification message to the user's mobile device. MessageBird's Verify API takes care of generating a random token, so you don't have to do this yourself. Codes are numeric and six digits by default. If you want to customize the length of the code or configure other options, you can refer to the [Verify API documentation](https://developers.messagebird.com/docs/verify#verify-request). 150 | 151 | The form we created in the last step submits the phone number via HTTP POST to `/step2`, so let's define this route in our `index.js`: 152 | 153 | ````javascript 154 | app.post('/step2', function(req, res) { 155 | var number = req.body.number; 156 | messagebird.verify.create(number, { 157 | originator : 'Code', 158 | template : 'Your verification code is %token.' 159 | }, function (err, response) { 160 | if (err) { 161 | console.log(err); 162 | res.render('step1', { 163 | error : err.errors[0].description 164 | }); 165 | } else { 166 | console.log(response); 167 | res.render('step2', { 168 | id : response.id 169 | }); 170 | } 171 | }) 172 | }); 173 | ```` 174 | 175 | Before we move on, let's quickly dive into what happens here: 176 | 177 | First, the number is taken from the request. Then, we're calling `messagebird.verify.create()` with the number as the first parameter. The second parameter is used to provide additional options: 178 | - With `originator` we can specify the sender ID of the text message with the code. The value should either be a valid telephone number (in international format, including country code) or an alphanumeric string with at most 11 characters. If we omitted this, it would default to the string _Code_. Please note that alphanumeric sender IDs do not work in a variety of countries including the United States so if you're expecting to send to these countries you _must_ set an originator. 179 | - Using `template` we can phrase the wording of the message. The template contains the placeholder `%token`, which is replaced with the generated token on MessageBird's end. If we omitted this, the message would simply contain the token and nothing else. 180 | 181 | Like most JavaScript functions, the MessageBird API call is asynchronous and transfers control to a callback function once finished. In the SDK's case, this callback function takes two parameters, `err` and `response`. 182 | 183 | If `err` is set, it means that an error has occurred. A typical error could be that the user has entered an invalid phone number. For our application, we simply re-render the page from our first step and pass the description of the error into the template - remember the `{{#if error}` section from the first step? In production applications, you'd most likely not expose the raw API error. Instead, you could consider different possible problems and return an appropriate message in your own words. You might also want to prevent some errors from occurring by doing some input validation on the phone number yourself. We've also added a `console.log(err)` statement so you can explore the complete error object in your console and learn more about how it works. 184 | 185 | In case the request was successful, we'll render a new page. Our API response contains an ID, which we'll need for the next step, so we'll just add it to the form. Since the ID is meaningless without your API access key there are no security implications of doing so, however, in practice, you'd be more likely to store this ID in a session object on the server. Just as before, we're logging the whole response to the console for debugging purposes. We still need to build the new page, so create a file called `step2.handlebars` in your `views` directory: 186 | 187 | ````html 188 | {{#if error}} 189 |

{{error}}

190 | {{/if}} 191 |

We have sent you a verification code!

192 |

Please enter the code here:

193 |
194 | 195 | 196 | 197 |
198 | ```` 199 | 200 | The form is very similar to the first step. Note that we include a hidden field with our verification ID and, once again, have a conditional error section. 201 | 202 | ## Verifying the code 203 | 204 | The user will check their phone and enter the code into our form. What we need to do next is send the user's input along with the ID of the verification request to MessageBird's API and see whether the verification was successful or not. Let's declare this third step as a new route in our `index.js`: 205 | 206 | ````javascript 207 | app.post('/step3', function(req, res) { 208 | var id = req.body.id; 209 | var token = req.body.token; 210 | messagebird.verify.verify(id, token, function(err, response) { 211 | if (err) { 212 | console.log(err); 213 | res.render('step2', { 214 | error: err.errors[0].description, 215 | id : id 216 | }); 217 | } else { 218 | console.log(response); 219 | res.render('step3'); 220 | } 221 | }) 222 | }); 223 | ```` 224 | 225 | This code looks very similar to the one in the second step. First, we're reading the input and then make a call to MessageBird's API. This time, it's the `messagebird.verify.verify()` method, which accepts `id` and `token` as its parameters. Inside the callback, error and success cases are handled. 226 | 227 | In case of an error, such as an invalid or expired token, we're showing that error on our page from the second step. 228 | 229 | In the success case, we simply show a new page. Create this page in your `views` directory and call it `step3.handlebars`: 230 | 231 | ````html 232 |

You have successfully verified your phone number.

233 | ```` 234 | 235 | ## Testing 236 | 237 | Let's write one more line of code in your `index.js` to run the Express application: 238 | 239 | ````javascript 240 | app.listen(8080); 241 | ```` 242 | 243 | Now, take a quick look at the directory structure you created. It should look something like this: 244 | 245 | ```` 246 | node_modules 247 | views 248 | - layouts 249 | - - main.handlebars 250 | - step1.handlebars 251 | - step2.handlebars 252 | - step3.handlebars 253 | index.js 254 | package-lock.json 255 | package.json 256 | ```` 257 | 258 | If you're all set, save your `index.js` and run the application from the command line: 259 | 260 | ```` 261 | node index.js 262 | ```` 263 | 264 | Point your browser to [http://localhost:8080/](http://localhost:8080/) and try to verify your own phone number. 265 | 266 | ## Nice work! 267 | 268 | You now have a running integration of MessageBird's Verify API! 269 | 270 | You can now leverage the flow, code snippets and UI examples from this tutorial to build your own two factor authentication system. Don't forget to download the code from the [MessageBird Developer Guides GitHub repository](https://github.com/messagebirdguides/verify-guide). 271 | 272 | 273 | ## Next steps 274 | 275 | Want to build something similar but not quite sure how to get started? Please feel free to let us know at support@messagebird.com, we'd love to help! 276 | -------------------------------------------------------------------------------- /views/layouts/main.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MessageBird Verify Example 5 | 6 | 7 |

MessageBird Verify Example

8 | 9 | {{{body}}} 10 | 11 | -------------------------------------------------------------------------------- /views/step1.handlebars: -------------------------------------------------------------------------------- 1 | {{#if error}} 2 |

{{error}}

3 | {{/if}} 4 |

Please enter your phone number (in international format, starting with +) to receive a verification code:

5 |
6 | 7 | 8 |
-------------------------------------------------------------------------------- /views/step2.handlebars: -------------------------------------------------------------------------------- 1 | {{#if error}} 2 |

{{error}}

3 | {{/if}} 4 |

We have sent you a verification code!

5 |

Please enter the code here:

6 |
7 | 8 | 9 | 10 |
-------------------------------------------------------------------------------- /views/step3.handlebars: -------------------------------------------------------------------------------- 1 |

You have successfully verified your phone number.

--------------------------------------------------------------------------------