From 8f7792d98eb708ecf7986938b4d5c770784dfb29 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Wed, 3 Mar 2021 15:22:06 -0800 Subject: [PATCH 01/14] Add initial code for direct-line-token sample --- .../k.direct-line-token/bot/.gitignore | 2 + .../k.direct-line-token/bot/package-lock.json | 1624 +++++++++++++++++ .../k.direct-line-token/bot/package.json | 20 + .../k.direct-line-token/bot/src/createBot.js | 25 + .../bot/src/createBotAdapter.js | 12 + .../k.direct-line-token/bot/src/index.js | 28 + .../k.direct-line-token/web/.gitignore | 2 + .../k.direct-line-token/web/package-lock.json | 1019 +++++++++++ .../k.direct-line-token/web/package.json | 21 + .../public/images/BotServices-Translucent.svg | 1 + .../web/public/images/BotServices.png | Bin 0 -> 3953 bytes .../k.direct-line-token/web/public/index.html | 79 + .../web/src/generateDirectLineToken.js | 20 + .../k.direct-line-token/web/src/index.js | 46 + .../web/src/routes/botMessages.js | 28 + .../web/src/routes/directLine/token.js | 24 + .../web/src/utils/fetchJSON.js | 18 + 17 files changed, 2969 insertions(+) create mode 100644 samples/01.getting-started/k.direct-line-token/bot/.gitignore create mode 100644 samples/01.getting-started/k.direct-line-token/bot/package-lock.json create mode 100644 samples/01.getting-started/k.direct-line-token/bot/package.json create mode 100644 samples/01.getting-started/k.direct-line-token/bot/src/createBot.js create mode 100644 samples/01.getting-started/k.direct-line-token/bot/src/createBotAdapter.js create mode 100644 samples/01.getting-started/k.direct-line-token/bot/src/index.js create mode 100644 samples/01.getting-started/k.direct-line-token/web/.gitignore create mode 100644 samples/01.getting-started/k.direct-line-token/web/package-lock.json create mode 100644 samples/01.getting-started/k.direct-line-token/web/package.json create mode 100644 samples/01.getting-started/k.direct-line-token/web/public/images/BotServices-Translucent.svg create mode 100644 samples/01.getting-started/k.direct-line-token/web/public/images/BotServices.png create mode 100644 samples/01.getting-started/k.direct-line-token/web/public/index.html create mode 100644 samples/01.getting-started/k.direct-line-token/web/src/generateDirectLineToken.js create mode 100644 samples/01.getting-started/k.direct-line-token/web/src/index.js create mode 100644 samples/01.getting-started/k.direct-line-token/web/src/routes/botMessages.js create mode 100644 samples/01.getting-started/k.direct-line-token/web/src/routes/directLine/token.js create mode 100644 samples/01.getting-started/k.direct-line-token/web/src/utils/fetchJSON.js diff --git a/samples/01.getting-started/k.direct-line-token/bot/.gitignore b/samples/01.getting-started/k.direct-line-token/bot/.gitignore new file mode 100644 index 0000000000..fde981cf4e --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/bot/.gitignore @@ -0,0 +1,2 @@ +/.env +/node_modules diff --git a/samples/01.getting-started/k.direct-line-token/bot/package-lock.json b/samples/01.getting-started/k.direct-line-token/bot/package-lock.json new file mode 100644 index 0000000000..ef8442ab46 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/bot/package-lock.json @@ -0,0 +1,1624 @@ +{ + "name": "bot", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@azure/ms-rest-js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.9.1.tgz", + "integrity": "sha512-F1crHKhmsvFLM9fsnDyCGFd2E2KR9GEZm5oBVV5D5k2EBQ7u7idtSJlSF6RDLDIrGWtc4NnFdYwsoiW8NLlBQg==", + "requires": { + "@types/tunnel": "0.0.0", + "axios": "^0.21.1", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "tunnel": "0.0.6", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + } + }, + "@netflix/nerror": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", + "integrity": "sha512-b+MGNyP9/LXkapreJzNUzcvuzZslj/RGgdVVJ16P2wSlYatfLycPObImqVJSmNAdyeShvNeM/pl3sVZsObFueg==", + "requires": { + "assert-plus": "^1.0.0", + "extsprintf": "^1.4.0", + "lodash": "^4.17.15" + }, + "dependencies": { + "extsprintf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=" + } + } + }, + "@types/jsonwebtoken": { + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz", + "integrity": "sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.17.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.54.tgz", + "integrity": "sha512-c8Lm7+hXdSPmWH4B9z/P/xIXhFK3mCQin4yCYMd2p1qpMG5AfgyJuYZ+3q2dT7qLiMMMGMd5dnkFpdqJARlvtQ==" + }, + "@types/tunnel": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz", + "integrity": "sha512-FGDp0iBRiBdPjOgjJmn1NH0KDLN+Z8fRmo+9J7XGBhubq1DPrGrbmG4UTlGzrpbCpesMqD0sWkzi27EYkOMHyg==", + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", + "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "requires": { + "@types/node": "*" + } + }, + "adal-node": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.2.1.tgz", + "integrity": "sha512-C/oasZuTy0NIqh5wPWjG/09XaG+zS7elC8upf1ZVExt9lSRncme4Ejbx8CKYk+wsGgj609y84txtRAXQVvqApg==", + "requires": { + "@types/node": "^8.0.47", + "async": "^2.6.3", + "date-utils": "*", + "jws": "3.x.x", + "request": "^2.88.0", + "underscore": ">= 1.3.1", + "uuid": "^3.1.0", + "xmldom": ">= 0.1.x", + "xpath.js": "~1.1.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + } + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "botbuilder": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/botbuilder/-/botbuilder-4.11.1.tgz", + "integrity": "sha512-6VtAtJzkAuuBXz2TwPT49yNeLkhpjfGouys24qPNZSBdl5UWkhniKo1t3DdIU8wPjlyGfqxmW8DQfUmfGI9elQ==", + "requires": { + "@azure/ms-rest-js": "1.9.1", + "@types/node": "^10.17.27", + "axios": "^0.21.1", + "botbuilder-core": "4.11.1", + "botframework-connector": "4.11.1", + "botframework-streaming": "4.11.1", + "filenamify": "^4.1.0", + "fs-extra": "^7.0.1", + "moment-timezone": "^0.5.28" + } + }, + "botbuilder-core": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/botbuilder-core/-/botbuilder-core-4.11.1.tgz", + "integrity": "sha512-Vtv2cUSXjVHZ3x081KFbqb/CpWfU24k2x89AAgsLWbTUytNW0QTRNgByjdznNl4CqV3b98mI6qJsk/fqFGnq4w==", + "requires": { + "assert": "^1.4.1", + "botframework-connector": "4.11.1", + "botframework-schema": "4.11.1" + } + }, + "botframework-connector": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/botframework-connector/-/botframework-connector-4.11.1.tgz", + "integrity": "sha512-oaQHGoF35kjTgczUM8MhJu9A4PWuUYVSvM6w0KKvVRL0You9ivHa0FT1JrUodxzLCY0ZWNVWdYijstXEq8feSQ==", + "requires": { + "@azure/ms-rest-js": "1.9.1", + "@types/jsonwebtoken": "7.2.8", + "adal-node": "0.2.1", + "base64url": "^3.0.0", + "botframework-schema": "4.11.1", + "cross-fetch": "^3.0.5", + "jsonwebtoken": "8.0.1", + "rsa-pem-from-mod-exp": "^0.8.4" + } + }, + "botframework-schema": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/botframework-schema/-/botframework-schema-4.11.1.tgz", + "integrity": "sha512-RDDEuh1yHnCqVppUIOh6pv51OQ7mN7wTPiBSvhIjhLW6zSJxDn7ho9QbJl1KJRnpQj4qnNvh1ZlrqTiL9QBIjA==" + }, + "botframework-streaming": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/botframework-streaming/-/botframework-streaming-4.11.1.tgz", + "integrity": "sha512-XZHQrsClzLzIIqWiv1FOrZri7vW21+xfIgrl4pueLmXZT8YM2FytW2EwnAyYIgooEk1CYbLZ/wmmgjxClLSATA==", + "requires": { + "@types/ws": "^6.0.3", + "uuid": "^3.4.0", + "ws": "^7.1.2" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "requires": { + "node-fetch": "2.6.1" + } + }, + "csv": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.2.tgz", + "integrity": "sha512-odDyucr9OgJTdGM2wrMbJXbOkJx3nnUX3Pt8SFOwlAMOpsUQlz1dywvLMXJWX/4Ib0rjfOsaawuuwfI5ucqBGQ==", + "requires": { + "csv-generate": "^3.2.4", + "csv-parse": "^4.8.8", + "csv-stringify": "^5.3.6", + "stream-transform": "^2.0.1" + } + }, + "csv-generate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.3.0.tgz", + "integrity": "sha512-EXSru4QwEWKwM7wwsJbhrZC+mHEJrhQFoXlohHs80CAU8Qhlv9gaw1sjzNiC3Hr3oUx5skDmEiAlz+tnKWV0RA==" + }, + "csv-parse": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.15.3.tgz", + "integrity": "sha512-jlTqDvLdHnYMSr08ynNfk4IAUSJgJjTKy2U5CQBSu4cN9vQOJonLVZP4Qo4gKKrIgIQ5dr07UwOJdi+lRqT12w==" + }, + "csv-stringify": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.2.tgz", + "integrity": "sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-utils": { + "version": "1.2.21", + "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", + "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debounce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", + "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-regexp-component": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", + "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "ewma": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ewma/-/ewma-2.0.1.tgz", + "integrity": "sha512-MYYK17A76cuuyvkR7MnqLW4iFYPEi5Isl2qb8rXiWpLiwFS9dxW/rncuNnjjgSENuVqZQkIuR4+DChVL4g1lnw==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=" + }, + "filenamify": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.2.0.tgz", + "integrity": "sha512-pkgE+4p7N1n7QieOopmn3TqJaefjdWXwEkj2XLZJLKfOgcQKkn11ahvGNgTD8mLggexLiDFQxeTs14xVU22XPA==", + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, + "filewatcher": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", + "integrity": "sha1-9KGVc1Xdr0Q8zXiolfPVXiPIoDQ=", + "dev": true, + "requires": { + "debounce": "^1.0.0" + } + }, + "find-my-way": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-2.2.5.tgz", + "integrity": "sha512-GjRZZlGcGmTh9t+6Xrj5K0YprpoAFCAiCPgmAH9Kb09O4oX6hYuckDfnDipYj+Q7B1GtYWSzDI5HEecNYscLQg==", + "requires": { + "fast-decode-uri-component": "^1.0.0", + "safe-regex2": "^2.0.0", + "semver-store": "^0.3.0" + } + }, + "follow-redirects": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonwebtoken": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.0.1.tgz", + "integrity": "sha1-UNrvjQqMfeLNBrwQE7dbBMzz8M8=", + "requires": { + "jws": "^3.1.4", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.0.0", + "xtend": "^4.0.1" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "requires": { + "mime-db": "1.46.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mixme": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.4.0.tgz", + "integrity": "sha512-B4Sm1CDC5+ov5AYxSkyeT5HLtiDgNOLKwFlq34wr8E2O3zRdTvQiLzo599Jt9cir6VJrSenOlgvdooVYCQJIYw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", + "requires": { + "moment": ">= 2.9.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-dev": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/node-dev/-/node-dev-6.4.0.tgz", + "integrity": "sha512-LMX3jLxKfaPgKiexjzWXCTU7x8wr7BtXfF/ZMC0CbECiON4jSNw9149YRUbGEIQm1GfQryPJ7fqPi0VPwFvGjw==", + "dev": true, + "requires": { + "dateformat": "^3.0.3", + "dynamic-dedupe": "^0.3.0", + "filewatcher": "~3.0.0", + "minimist": "^1.1.3", + "node-notifier": "^8.0.1", + "resolve": "^1.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-notifier": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "requires": { + "safe-buffer": "^5.2.1" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "restify": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/restify/-/restify-8.5.1.tgz", + "integrity": "sha512-g+xciouvSDg2vmCZuCinztt2mvQynCfnGIE1y8vMjfcUrjBo4AP8DJ9RNheu0mdGpiI0cMoCHYA/GdZ3TEW+DA==", + "requires": { + "assert-plus": "^1.0.0", + "bunyan": "^1.8.12", + "csv": "^5.1.1", + "dtrace-provider": "^0.8.1", + "escape-regexp-component": "^1.0.2", + "ewma": "^2.0.1", + "find-my-way": "^2.0.1", + "formidable": "^1.2.1", + "http-signature": "^1.2.0", + "lodash": "^4.17.11", + "lru-cache": "^5.1.1", + "mime": "^2.4.3", + "negotiator": "^0.6.2", + "once": "^1.4.0", + "pidusage": "^2.0.17", + "qs": "^6.7.0", + "restify-errors": "^8.0.2", + "semver": "^6.1.1", + "send": "^0.16.2", + "spdy": "^4.0.0", + "uuid": "^3.3.2", + "vasync": "^2.2.0" + }, + "dependencies": { + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + } + } + }, + "restify-errors": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/restify-errors/-/restify-errors-8.0.2.tgz", + "integrity": "sha512-UsXUVQo7M26xoQzeUcZQ0+H8L2t9DGzrXcAgR3WB/1vnbl+UdI4tZ1PqYsN+sS5WnqHKZ0Xy9w0CKf83bbrwYA==", + "requires": { + "@netflix/nerror": "^1.0.0", + "assert-plus": "^1.0.0", + "lodash": "^4.17.15", + "safe-json-stringify": "^1.0.4" + } + }, + "ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "rsa-pem-from-mod-exp": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.4.tgz", + "integrity": "sha1-NipCxtMEBW1JOz8SvOq7LGV2ptQ=" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "requires": { + "ret": "~0.2.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz", + "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "stream-transform": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.4.tgz", + "integrity": "sha512-LQXH1pUksoef5Ijo6+2ihnjLLZtZuoqu1vhut6a7xZ77nrLA/shbbx2FAzVC/nkb6wwrPzOO98700mv4HDQcWg==", + "requires": { + "mixme": "^0.4.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "underscore": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", + "integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "vasync": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.0.tgz", + "integrity": "sha1-z951GGChWCLbOxMrxZsRakra8Bs=", + "requires": { + "verror": "1.10.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", + "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==" + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "xmldom": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz", + "integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA==" + }, + "xpath.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", + "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/bot/package.json b/samples/01.getting-started/k.direct-line-token/bot/package.json new file mode 100644 index 0000000000..aedd45b51c --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/bot/package.json @@ -0,0 +1,20 @@ +{ + "name": "bot", + "version": "1.0.0", + "description": "", + "main": "src/index.js", + "scripts": { + "start": "node-dev --no-notify --respawn .", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Microsoft Corporation", + "license": "MIT", + "dependencies": { + "botbuilder": "^4.11.1", + "dotenv": "^8.2.0", + "restify": "^8.5.1" + }, + "devDependencies": { + "node-dev": "^6.4.0" + } +} diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js b/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js new file mode 100644 index 0000000000..2e7ca70bc8 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js @@ -0,0 +1,25 @@ +const { ActivityHandler } = require('botbuilder'); + +module.exports = () => { + const bot = new ActivityHandler(); + + // Handler for "message" activity + bot.onMessage(async (context, next) => { + await context.sendActivity(`Your user ID is ${context.activity.from.id}`); + await next(); + }); + + // Handler for when members are added to the conversation + bot.onMembersAdded(async (context, next) => { + const wasNonBotMemberAdded = context.activity.membersAdded.some( + channelAccount => channelAccount.id !== context.activity.recipient.id); + + if (wasNonBotMemberAdded) { + await context.sendActivity(`Hello! Your user ID is ${context.activity.from.id}`); + } + + await next(); + }); + + return bot; +}; diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/createBotAdapter.js b/samples/01.getting-started/k.direct-line-token/bot/src/createBotAdapter.js new file mode 100644 index 0000000000..e4d00a3cb7 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/bot/src/createBotAdapter.js @@ -0,0 +1,12 @@ +const { BotFrameworkAdapter } = require('botbuilder'); + +const { MICROSOFT_APP_ID, MICROSOFT_APP_PASSWORD } = process.env; + +module.exports = function createBotAdapter() { + const adapter = new BotFrameworkAdapter({ + appId: MICROSOFT_APP_ID, + appPassword: MICROSOFT_APP_PASSWORD + }); + + return adapter; +}; diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/index.js b/samples/01.getting-started/k.direct-line-token/bot/src/index.js new file mode 100644 index 0000000000..70beea487b --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/bot/src/index.js @@ -0,0 +1,28 @@ +require('dotenv').config(); + +// Checks for required environment variables. +[ + 'MICROSOFT_APP_ID', + 'MICROSOFT_APP_PASSWORD' +].forEach(name => { + if (!process.env[name]) { + throw new Error(`Environment variable ${name} must be set.`); + } +}); + +const { createServer } = require('restify'); +const createBot = require('./createBot'); +const createBotAdapter = require('./createBotAdapter'); + +const PORT = 3978; +const adapter = createBotAdapter(); +const bot = createBot(); +const server = createServer(); + +server.post('/api/messages', (req, res) => { + adapter.processActivity(req, res, context => bot.run(context)); +}); + +server.listen(PORT, () => { + console.log(`Bot is now listening to port ${PORT}`); +}); diff --git a/samples/01.getting-started/k.direct-line-token/web/.gitignore b/samples/01.getting-started/k.direct-line-token/web/.gitignore new file mode 100644 index 0000000000..fde981cf4e --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/.gitignore @@ -0,0 +1,2 @@ +/.env +/node_modules diff --git a/samples/01.getting-started/k.direct-line-token/web/package-lock.json b/samples/01.getting-started/k.direct-line-token/web/package-lock.json new file mode 100644 index 0000000000..9e9ca02f3c --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/package-lock.json @@ -0,0 +1,1019 @@ +{ + "name": "web", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@netflix/nerror": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", + "integrity": "sha512-b+MGNyP9/LXkapreJzNUzcvuzZslj/RGgdVVJ16P2wSlYatfLycPObImqVJSmNAdyeShvNeM/pl3sVZsObFueg==", + "requires": { + "assert-plus": "^1.0.0", + "extsprintf": "^1.4.0", + "lodash": "^4.17.15" + }, + "dependencies": { + "extsprintf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=" + } + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "csv": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.2.tgz", + "integrity": "sha512-odDyucr9OgJTdGM2wrMbJXbOkJx3nnUX3Pt8SFOwlAMOpsUQlz1dywvLMXJWX/4Ib0rjfOsaawuuwfI5ucqBGQ==", + "requires": { + "csv-generate": "^3.2.4", + "csv-parse": "^4.8.8", + "csv-stringify": "^5.3.6", + "stream-transform": "^2.0.1" + } + }, + "csv-generate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.3.0.tgz", + "integrity": "sha512-EXSru4QwEWKwM7wwsJbhrZC+mHEJrhQFoXlohHs80CAU8Qhlv9gaw1sjzNiC3Hr3oUx5skDmEiAlz+tnKWV0RA==" + }, + "csv-parse": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.15.3.tgz", + "integrity": "sha512-jlTqDvLdHnYMSr08ynNfk4IAUSJgJjTKy2U5CQBSu4cN9vQOJonLVZP4Qo4gKKrIgIQ5dr07UwOJdi+lRqT12w==" + }, + "csv-stringify": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.2.tgz", + "integrity": "sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debounce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", + "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-regexp-component": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", + "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "ewma": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ewma/-/ewma-2.0.1.tgz", + "integrity": "sha512-MYYK17A76cuuyvkR7MnqLW4iFYPEi5Isl2qb8rXiWpLiwFS9dxW/rncuNnjjgSENuVqZQkIuR4+DChVL4g1lnw==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, + "filewatcher": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", + "integrity": "sha1-9KGVc1Xdr0Q8zXiolfPVXiPIoDQ=", + "dev": true, + "requires": { + "debounce": "^1.0.0" + } + }, + "find-my-way": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-2.2.5.tgz", + "integrity": "sha512-GjRZZlGcGmTh9t+6Xrj5K0YprpoAFCAiCPgmAH9Kb09O4oX6hYuckDfnDipYj+Q7B1GtYWSzDI5HEecNYscLQg==", + "requires": { + "fast-decode-uri-component": "^1.0.0", + "safe-regex2": "^2.0.0", + "semver-store": "^0.3.0" + } + }, + "follow-redirects": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==" + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-signature": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.5.tgz", + "integrity": "sha512-NwoTQYSJoFt34jSBbwzDHDofoA61NGXzu6wXh95o1Ry62EnmKjXb/nR/RknLeZ3G/uGwrlKNY2z7uPt+Cdl7Tw==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.14.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mixme": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.4.0.tgz", + "integrity": "sha512-B4Sm1CDC5+ov5AYxSkyeT5HLtiDgNOLKwFlq34wr8E2O3zRdTvQiLzo599Jt9cir6VJrSenOlgvdooVYCQJIYw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "optional": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-dev": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/node-dev/-/node-dev-6.4.0.tgz", + "integrity": "sha512-LMX3jLxKfaPgKiexjzWXCTU7x8wr7BtXfF/ZMC0CbECiON4jSNw9149YRUbGEIQm1GfQryPJ7fqPi0VPwFvGjw==", + "dev": true, + "requires": { + "dateformat": "^3.0.3", + "dynamic-dedupe": "^0.3.0", + "filewatcher": "~3.0.0", + "minimist": "^1.1.3", + "node-notifier": "^8.0.1", + "resolve": "^1.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-notifier": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "requires": { + "safe-buffer": "^5.2.1" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "restify": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/restify/-/restify-8.5.1.tgz", + "integrity": "sha512-g+xciouvSDg2vmCZuCinztt2mvQynCfnGIE1y8vMjfcUrjBo4AP8DJ9RNheu0mdGpiI0cMoCHYA/GdZ3TEW+DA==", + "requires": { + "assert-plus": "^1.0.0", + "bunyan": "^1.8.12", + "csv": "^5.1.1", + "dtrace-provider": "^0.8.1", + "escape-regexp-component": "^1.0.2", + "ewma": "^2.0.1", + "find-my-way": "^2.0.1", + "formidable": "^1.2.1", + "http-signature": "^1.2.0", + "lodash": "^4.17.11", + "lru-cache": "^5.1.1", + "mime": "^2.4.3", + "negotiator": "^0.6.2", + "once": "^1.4.0", + "pidusage": "^2.0.17", + "qs": "^6.7.0", + "restify-errors": "^8.0.2", + "semver": "^6.1.1", + "send": "^0.16.2", + "spdy": "^4.0.0", + "uuid": "^3.3.2", + "vasync": "^2.2.0" + } + }, + "restify-errors": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/restify-errors/-/restify-errors-8.0.2.tgz", + "integrity": "sha512-UsXUVQo7M26xoQzeUcZQ0+H8L2t9DGzrXcAgR3WB/1vnbl+UdI4tZ1PqYsN+sS5WnqHKZ0Xy9w0CKf83bbrwYA==", + "requires": { + "@netflix/nerror": "^1.0.0", + "assert-plus": "^1.0.0", + "lodash": "^4.17.15", + "safe-json-stringify": "^1.0.4" + } + }, + "ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "requires": { + "ret": "~0.2.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz", + "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + } + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "stream-transform": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.4.tgz", + "integrity": "sha512-LQXH1pUksoef5Ijo6+2ihnjLLZtZuoqu1vhut6a7xZ77nrLA/shbbx2FAzVC/nkb6wwrPzOO98700mv4HDQcWg==", + "requires": { + "mixme": "^0.4.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "vasync": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.0.tgz", + "integrity": "sha1-z951GGChWCLbOxMrxZsRakra8Bs=", + "requires": { + "verror": "1.10.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/web/package.json b/samples/01.getting-started/k.direct-line-token/web/package.json new file mode 100644 index 0000000000..acbe0cf62a --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/package.json @@ -0,0 +1,21 @@ +{ + "name": "web", + "version": "1.0.0", + "description": "", + "main": "src/index.js", + "scripts": { + "start": "node-dev --no-notify --respawn .", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Microsoft Corporation", + "license": "MIT", + "dependencies": { + "dotenv": "^8.2.0", + "http-proxy": "^1.18.1", + "node-fetch": "^2.6.1", + "restify": "^8.5.1" + }, + "devDependencies": { + "node-dev": "^6.4.0" + } +} diff --git a/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices-Translucent.svg b/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices-Translucent.svg new file mode 100644 index 0000000000..60ac27ba90 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices-Translucent.svg @@ -0,0 +1 @@ +Asset 5 diff --git a/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices.png b/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc45d0fddd7c063fa52f733ccd8de10de78fa5a GIT binary patch literal 3953 zcmaJ^dpwiv{~vN{NJ@E#W;7kxY-5f~Y?QN{lhc}Ev;!OF6j?~D9AXj?MHDILLz)y7 zjSz(xA*VzKrNnQZ@6+@B{qgH}zwZ0K4zKt7{ds?`>vP?&*PUc%b5wGx{8j(}AZdOK zYtL_8Hy<$({yQjBQ;XmHMK^V&J5YS-VYpxdz}Sa!iU2Yv;rs~p1e{NJ&@+M|03f7J zbabV=T3hMkDI_Rv69Z+CsC+g6U}(gk;_!h4I_MO^k4Q#?KQ=uCgNQz8u#2{}rZp8q z@FyON2qrj0*f`=N0`Yo2V57qzLxw(IfJC62LG+A&+l(e!@!`wA@o2r z_&=sxt?fV+C1A#^mD@AN+@kf{I4l4<`e6Mw;A3>*~(hiY!7^c!ex{r^Kr zq<_&gx;^2)e*aHlnqxSX0JA61C?Uak{>J&LY=)xhV}c1dIwjbVLOK0=747^fbPCO% zLIs(gCXfhZIt>KZhH8TLyAa7flrY+XznRw7`sQRB9Y@9!%&}-NUkgek`sf=Q>uG80 zVoh{(Feo_ORL>Z$hcv~Q>Y89MTAIc>$lqKn1s}4h{hRCaUoPg4+|8mOQTdUvgkWMQ z!N)9^LIVArygu>IUiAK`_Yc?S&t8oG$c6FEz&5w|e{K2iDSjPocKRh(OmBi&9pz+mfSi|D~8akWx0 z;^Xh%HkJ5!DW$14eS4t#@j-L*2r^?Na%}W%)6ch6e%*Ru-_$F$t`j;_KjxBU^-xwm z647Ejq4u?u)>%3_i+o)rWwBA=Qtv}NJvx?akaiCQ5;==ZrVXIR6=VG0Af#t|pmG*R z!mPwS{WWtB0DJ|QorOmraVrj;C}k!~goYZ(+71@0N5T9lpGd zemu_F!oWv!HhkL!+?UsC`|jUot@d;FzMSe;UaUDO_h>H-D0MgD)Lj2krAYlmrbqaR zj)nZ~9itX(FTyPL>AEG$OI*b3_(?Z9gCUS2=A}!d=UH9|E0Jy$bb%R6s2DuJH z;a^OWXV3H=F4&S?pN&1f0=V`IA{rD(dh>W&uMF?D&N(xd?1Zw3_jZq*pfJbw(ZYx9 z*@cfQ$0IG@NxsjQyVI<5|HW+o{X;XtFGRfAUsReTC1Q3j(ii1Vqh+=(HGefw(;W%F zW^r}Tdpt(WHJRg54%HaS>{^G-L+X*XfUB0QJZZmLXBkRn$W+Uvggv~RX`aS0%7aen z^ZRm6Akj-{j<+pJ%Fg#sM9zL04jLIX*7kJu@@x)x3aB90E*7=*W^)9GpKs5SKH8ik zRpdr+m^mm_pBsSMhdNSUUD5oVeL$Eo-OlpG9UXV*^*$dozv1uR!itS1-%>xIW1Sc= z78TG|6m%=Kd%*zz@x?~AcBhR#XM54O#OD)piPd{WN4*zB~|7O)i=bD!CRQh=4 zx$j~45x0DUieK&P@@hR^Tn*je0r|S;cI-LTE0vW{UQCvxN*VEfOE@-?mli>SBbOmK|CZe(X4`~K~tKSd*4W?t%)htDMLGP=Zf~w&zotQL(R3z&zBQj zMS9|D_P*cSz4h+RgctpfQr2>Q968=sAW$(PE#DoVO3|_vT{LF0TU_1fxddtRPAxB< zWkl*YK%Q$V>0-O)!vTYPs&%dc-Ajbo<0lo>byA6ZkEA(HE}}PUbf7v~U3X15>1C_{ zcU;l_uZKr6){<}+=5qw&^VBiswXFX5c@<_|4z%m+%1=>MaG6_NpFmL6+`eN@UAR*} z=02rw+i~g<>Ld|=uAlw$r#GUG&=D`VY9JpL%gnV6+^-Q~M3%}L>wLNx;m*Fu*{!y9 z1b5wo-k?e8Q&j*x{&yS1g{_}+#WcHclx4A`T4D7YRiUg(k298mfzDR3pY2Ah5>0AiwtTb-8#6(a!g(9p zl*8F>C*x`QhZSfU^Ps`0!2Q)TU*<2!jMiQHQT(+-$~MeivZBn`=IqNzd#0VL=m#6G zXQgsqdAvs#<1F^odr&48tpvjgR6VkM+TWI&uMh~LhpNGX0=r6UL~3iGyfKd91v^am zGEhH8c;(X|U5NjkknO`Y-p95oee(W*R!T|?k$=*>dw4Ax0C|V2fV3@6ghkJzk42u? zee_qLcs06As~1%k0Z^9&%4~byZRGO;&4wan`z(r5-=%ri1a!S#%Sbr7g{SBcYE#(O zP<9Pu71hghc<)EOHYd{*?Y}l6N!m>pl5lsg$U-jUxh~eHS%sWUm%je(TJUQVe6eB9 z@hoTV4qoi9fjuIHC#61*SH!QEj%=0b>tOUZ|18d7;)*&-;5FisW{mJBpZ%9G`2@>M zaNXUgRxVRyY|ePbrif-{nYacwwO8tvTGbWpyNiS(mMu*SXJ=1%W$u*4I@Nzx+2lT zSj+%ah;xlf&kXiXxfI6G_Fztzy48JN@Of{$B`<;DioZ1#oOuCq;Iy{|AS<`TGxj{b zb?2e(<&c zMZlHCL$h*{erleHQ7?q^AMWF$_K$6E$hi@!!8su{S$r(9xhn8g)ZU>>D6iawHF2U4 zNA5)@8*p2=nyJ1IFeti4Ldj>`Ld39I7TUDdDvXD-`AQdg&5%B=j0Srj6N?XR}A#%OdgRg&W;2>|KcrI=4+B0N&~sF5Q^neXCUrWv-{d znvcCYlc*P6FZafYUa#ZOYW{eT?7gIz*~EU_Hm_z)SPfs-W)()66KFjSgIcPCQj}u%;J|*)13{0O1WrrZaC=6 zbinEJi%XJkIi~LmN~cfc1zH20B2rfnm#WGGOf0`X!BVFU-;%cH8zym$9TFpqn}^>` zz)FEY*Fy6%4=V2Xgzb$jf{f*BU9pIBh^s>TH{BwYIoTv81(e5qb)6Ux;yuv&>D)K2 zQZp8ZmQFKJ$(;me`*YB;!fkmi?Mj7{{lErJQE>xF??h0Hb92r!-_WHJ`pVUH=YU&T z&mnkczwxxV{Gy2XG&;=ib>wxsy$unu3W$_fp4O}19IH<&=$z6{gv_h=-^s`Tw$ru_ z%|~vGit=X88RY@GkuTSnWDm00j+!@<_36Z7Ju%)xJ7KGdoQkg!Ge!_@DOnt$+wcJENE|ka-k@u~%O5Pi2rP4o#*12p96L6W5=`&59=qF@kZhQFSXAV*NaxN` z)V2VNe3Ft)O;j_>B3*2WUHsL%Zs=_JQ#&%J4jB89zGV>y8H0UK5@J<#kHJJ=Utrf= zQVfaezk;K-%kSZu?cCuH056^^OnDnnQkOMO+LdV)Tcpa2rZ04so6UZ*dMN&uwBsLQ%3;l@+z1Cw(xX;vFRx%M1}p?8}+&*4K1rLTXdP{rs9 zdn#@2@=pYiZm}vIsJv%{0TNUwZT@Hc*p$@ DT{Q~f literal 0 HcmV?d00001 diff --git a/samples/01.getting-started/k.direct-line-token/web/public/index.html b/samples/01.getting-started/k.direct-line-token/web/public/index.html new file mode 100644 index 0000000000..2fe08b3dc6 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/public/index.html @@ -0,0 +1,79 @@ + + + + + + + + Web Chat Direct Line Token Demo + + + + + + + +
+ +
+ + diff --git a/samples/01.getting-started/k.direct-line-token/web/src/generateDirectLineToken.js b/samples/01.getting-started/k.direct-line-token/web/src/generateDirectLineToken.js new file mode 100644 index 0000000000..fb29fb3f40 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/src/generateDirectLineToken.js @@ -0,0 +1,20 @@ +const fetchJSON = require('./utils/fetchJSON'); + +// Generates a new Direct Line token given the secret. +// Provides user ID in the request body to bind the user ID to the token. +module.exports = async function generateDirectLineToken(secret, userId) { + const { token } = await fetchJSON('https://directline.botframework.com/v3/directline/tokens/generate', { + headers: { + authorization: `Bearer ${secret}`, + 'Content-Type': 'application/json' + }, + method: 'POST', + body: JSON.stringify({ + user: { + id: userId + } + }) + }); + + return token; +}; diff --git a/samples/01.getting-started/k.direct-line-token/web/src/index.js b/samples/01.getting-started/k.direct-line-token/web/src/index.js new file mode 100644 index 0000000000..1858ea96d5 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/src/index.js @@ -0,0 +1,46 @@ +require('dotenv').config(); + +// Setting default environment variables. +process.env = { + PORT: '5000', + STATIC_FILES: 'public', + ...process.env +}; + +// Checks for required environment variables. +[ + 'DIRECT_LINE_SECRET' +].forEach(name => { + if (!process.env[name]) { + throw new Error(`Environment variable ${name} must be set.`); + } +}); + +const { join } = require('path'); +const restify = require('restify'); + +const server = restify.createServer(); +const { PORT, STATIC_FILES } = process.env; + +server.use(restify.plugins.queryParser()); + +// Registering routes. +server.get('/api/directline/token', require('./routes/directLine/token')); +server.post('/api/messages', require('./routes/botMessages')); + +// We will use the REST API server to serve static web content to simplify deployment for demonstration purposes. +STATIC_FILES && + server.get( + '/**/*', + restify.plugins.serveStatic({ + default: 'index.html', + directory: join(__dirname, '..', STATIC_FILES) + }) + ); + + server.listen(PORT, () => { + STATIC_FILES && console.log(`Will serve static content from ${STATIC_FILES}`); + + console.log(`Rest API server is listening to port ${PORT}`); + }); + \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/web/src/routes/botMessages.js b/samples/01.getting-started/k.direct-line-token/web/src/routes/botMessages.js new file mode 100644 index 0000000000..cfffd656bd --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/src/routes/botMessages.js @@ -0,0 +1,28 @@ +const { URL } = require('url'); +const httpProxy = require('http-proxy'); + +// To simplify deployment for our demo, we aggregate web and bot server into a single endpoint. +// If the HTTP POST is going to /api/messages, we will reverse-proxy the request to the bot server at http://localhost:3978/. + +const { PROXY_BOT_URL } = process.env; + +if (PROXY_BOT_URL) { + const proxy = httpProxy.createProxyServer(); + + console.log(`Will redirect /api/messages to ${new URL('api/messages', PROXY_BOT_URL).href}`); + + module.exports = (req, res) => { + proxy.web(req, res, { target: PROXY_BOT_URL }); + }; +} else { + let warningShown; + + module.exports = (_, res) => { + if (!warningShown) { + warningShown = true; + console.warn('PROXY_BOT_URL is not set, we are not reverse-proxying /api/messages.'); + } + + res.send(502); + }; +} diff --git a/samples/01.getting-started/k.direct-line-token/web/src/routes/directLine/token.js b/samples/01.getting-started/k.direct-line-token/web/src/routes/directLine/token.js new file mode 100644 index 0000000000..63b7cb9526 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/src/routes/directLine/token.js @@ -0,0 +1,24 @@ +const generateDirectLineToken = require('../../generateDirectLineToken'); + +const crypto = require('crypto'); +const { promisify } = require('util'); + +const randomBytesAsync = promisify(crypto.randomBytes); + +const { DIRECT_LINE_SECRET } = process.env; + +// GET /api/directline/token +// Generates a new Direct Line token +module.exports = async (_, res) => { + // Generate a random user ID to use for DirectLine token + const randomUserId = await generateRandomUserId(); + + res.json({ token: await generateDirectLineToken(DIRECT_LINE_SECRET, randomUserId) }); +}; + +// Generates a random user ID +// Prefixed with "dl_", as required by the Direct Line API +async function generateRandomUserId() { + const buffer = await randomBytesAsync(16); + return `dl_${buffer.toString('hex')}`; +} \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/web/src/utils/fetchJSON.js b/samples/01.getting-started/k.direct-line-token/web/src/utils/fetchJSON.js new file mode 100644 index 0000000000..9814c30216 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/web/src/utils/fetchJSON.js @@ -0,0 +1,18 @@ +const fetch = require('node-fetch'); + +// Helper function for fetching network resource as JSON +module.exports = async function fetchJSON(url, options) { + const res = await fetch(url, { + ...options, + headers: { + ...options.headers, + accept: 'application/json' + } + }); + + if (!res.ok) { + throw new Error(`Failed to fetch JSON from server due to ${res.status}`); + } + + return await res.json(); +}; From 1d2223f87e93b72e732869f5ab66d1a27f98399f Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 4 Mar 2021 08:36:59 -0800 Subject: [PATCH 02/14] Add README to new direct-line-token sample --- .../k.direct-line-token/README.md | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 samples/01.getting-started/k.direct-line-token/README.md diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md new file mode 100644 index 0000000000..31777c2320 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -0,0 +1,200 @@ +# Direct Line Token Sample + +![samples/01.k.direct-line-token](https://github.com/microsoft/BotFramework-WebChat/workflows/samples/01.k.direct-line-token/badge.svg) + +# Description + +This sample demonstrates how to integrate Web Chat in a way that 1) does not expose your Direct Line secret to the browser, and 2) mitigates user impersonation by not allowing the client to set its own user ID. + +See the [Motivation](#Motivation) section below for more background on these issues. + +# Test out the hosted sample + +There is no hosted demo for this sample yet. + +# How to run locally + +This demo includes a bot that you will run locally, so before running the code, you will have to set up an Azure Bot Service resource. + +1. [Clone the code](#clone-the-code) +1. [Setup Azure Bot Services](#setup-azure-bot-services) +1. [Prepare and run the code](#prepare-and-run-the-code) + +## Clone the code + +To host this demo, you will need to clone the code and run locally. + +1. Clone this repository +1. Create two empty files for environment variables, `/bot/.env` and `/web/.env` + +## Setup Azure Bot Services + +> We prefer to use [Bot Channel Registration](https://ms.portal.azure.com/#create/Microsoft.BotServiceConnectivityGalleryPackage) during development. This will help you diagnose problems locally without deploying to the server and speed up development. + +You can follow our instructions on how to [setup a new Bot Channel Registration](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-3.0). + +1. Save the Microsoft App ID and password to `/bot/.env` + - `MICROSOFT_APP_ID=12345678-1234-5678-abcd-12345678abcd` + - `MICROSOFT_APP_PASSWORD=a1b2c3d4e5f6` +1. Save the Web Chat secret to `/web/.env` + - `DIRECT_LINE_SECRET=a1b2c3.d4e5f6g7h8i9j0` + +During development, you will run your bot locally. Azure Bot Services will send activities to your bot thru a public URL. You can use [ngrok](https://ngrok.com/) to expose your bot server on a public URL. + +1. Run `ngrok http -host-header=localhost:3978 3978` +1. Update your Bot Channel Registration. You can use [Azure CLI](https://aka.ms/az-cli) or [Azure Portal](https://portal.azure.com) + - Via Azure CLI + - Run `az bot update --resource-group --name --subscription --endpoint "https://a1b2c3d4.ngrok.io/api/messages"` + - Via Azure Portal + - Browse to your Bot Channel Registration + - Select "Settings" + - In "Configuration" section, set "Messaging Endpoint" to `https://a1b2c3d4.ngrok.io/api/messages` + +## Prepare and run the code + +1. Under each of `bot`, and `web` folder, run the following + 1. `npm install` + 1. `npm start` +1. Browse to http://localhost:5000/ to start the demo + +# Things to try out + +- Type anything to the bot. It should reply with your user ID, which will stay the same for the duration of the session. +- Open a new browser tab to http://localhost:5000 and type anything to the bot. It should reply with a different user ID since it has generated a different Direct Line token. + +# Code + +The code is organized into two separate folders: + +- `/bot/` is the bot server +- `/web/` is the REST API for generating Direct Line tokens + - `GET /api/directline/token` will generate a new Direct Line token for the app. The token will be bound to a random user ID. + - During development-time, it will also serve the bot server via `/api/messages/` + - To enable this feature, add `PROXY_BOT_URL=http://localhost:3978` to `/web/.env` + +## Constructing the user ID + +In this sample, the user is anonymous, so the API randomly generates a user ID: + +```js +// web/src/routes/directLine/token.js + +async function generateRandomUserId() { + const buffer = await randomBytesAsync(16); + return `dl_${buffer.toString('hex')}`; +} +``` + +The user ID is prefixed with "dl_" as required by the [Direct Line token API](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0#generate-token). + +## Retrieving a user-specific Direct Line token + +The backend API calls the Direct Line API to retrieve a Direct Line token. Notice that we pass the user ID in the body of the request: + +```js +// web/src/generateDirectLineToken.js + +async function generateDirectLineToken(secret, userId) { + const { token } = await fetchJSON('https://directline.botframework.com/v3/directline/tokens/generate', { + headers: { + authorization: `Bearer ${secret}`, + 'Content-Type': 'application/json' + }, + method: 'POST', + body: JSON.stringify({ + user: { + id: userId + } + }) + }); + + return token; +}; +``` + +The resulting Direct Line token will be bound to the passed user ID. + +## Calling the API and rendering Web Chat + +The UI calls the API and uses the resulting Direct Line token to render Web Chat: + +```js +// web/public/index.html + +const { token } = await fetchJSON('/api/directline/token'); + +WebChat.renderWebChat( + { + directLine: WebChat.createDirectLine({ token }), + styleOptions: { + backgroundColor: 'rgba(255, 255, 255, .8)' + } + }, + document.getElementById('webchat') +); +``` + +Note that we do *not* specify a user ID when initiating Web Chat. Direct Line will handle sending the user ID to the bot based on the token. + +# Overview + +This sample includes multiple parts: + +- **The UI** is a static HTML/JS web page with Web Chat integrated via JavaScript bundle. It makes a POST request to the backend API and uses the resulting Direct Line token to render Web Chat. +- **The backend API** generates Direct Line tokens. Each generated token is bound to a new, randomly-generated user ID. +- **The bot** is a bare-bones bot that responds to every message by sending the user's ID. + +## Motivation + +### Hiding the Web Chat secret + +When embedding Web Chat into a site, you must provide either your Direct Line secret or a Direct Line token so that Web Chat can communicate with the bot. The Direct Line secret can be used to access all of the bot's conversations, and it doesn't expire. A Direct Line token can only be used to access a single conversation, and it does expire. See the [Direct Line Authentication documentation](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0) for more information. + +Therefore, embedding Web Chat using the Direct Line secret directly is strongly discouraged because it would expose your secret on the client-side. Instead, the recommended approach is to exchange the secret for a Direct Line token on the server-side. This sample shows how to obtain and use the token. + +### Avoiding user impersonation + +Web Chat allows you to specify a user ID on the client-side, which will be sent in activities to the bot. However, this is susceptible to user impersonation because a malicious user could modify their user ID. Since the user ID typically isn't verified, this is a security risk if the bot stores sensitive data keyed on the user ID. For example, the built-in [user authentication support in Azure Bot Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-authentication?view=azure-bot-service-4.0) associates access tokens with user IDs. + +To avoid impersonation, the recommended approach is for the server to bind a user ID to the Direct Line token. Then any conversation using that token will send the bound user ID to the bot. However, if the client is going to provide the user ID to the server, it is important for the server to validate the ID somehow (see below). Otherwise, a malicious user could still modify the user ID being sent by the client. + +To keep things simple, this sample generates a random user ID on the server-side and binds it to the Direct Line token. While this mitigates impersonation concerns, the downside is that users will have a different ID every time they talk to the bot. + +## Content of the `.env` files + +The `.env` file hold the environment variable critical to run the service. These are usually security-sensitive information and must not be committed to version control. Although we recommend to keep them in [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/), for simplicity of this sample, we would keep them in `.env` files. + +To ease the setup of this sample, here is the template of `.env` files. + +### `/bot/.env` + +``` +MICROSOFT_APP_ID=12345678-1234-5678-abcd-12345678abcd +MICROSOFT_APP_PASSWORD=a1b2c3d4e5f6 +``` + +### `/web/.env` + +``` +DIRECT_LINE_SECRET=a1b2c3.d4e5f6g7h8i9j0 +``` + +# Frequently asked questions + +## What if I need a consistent user ID across sessions/devices? + +Instead of randomly generating user IDs, the backend API could leverage a user's existing identity from a true identity provider. The user would first sign in to the site before talking to the bot. That way, if the user signs in using the same identity on a different browser or device, the user ID will be the same. This also prevents user impersonation because we can verify the user's identity with the identity provider. + +The flow could be: + +1. The user signs in to the web app. +1. The web app calls the backend API for generating Direct Line tokens, providing a verifiable user token. +1. The backend API verifies the user token with the identity provider. +1. The backend API uses the token to get an ID for the user. (The specifics will vary based on the identity provider and type of token.) +1. The backend API generates a Direct Line token bound to the user ID (just as this sample does) and returns it to the web app. + +# Further reading + +- [Setting up a new Bot Channel Registration](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-3.0) +- [Generating a Direct Line token](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0#generate-token) +- [Enhanced Direct Line Authentication feature](https://blog.botframework.com/2018/09/25/enhanced-direct-line-authentication-features/) From c33faa4c7097dd464642dbfc449bb6e63a811d74 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 4 Mar 2021 08:43:21 -0800 Subject: [PATCH 03/14] Fix indentation issues --- .../k.direct-line-token/bot/src/createBot.js | 2 +- .../k.direct-line-token/web/src/index.js | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js b/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js index 2e7ca70bc8..9d82cdf359 100644 --- a/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js +++ b/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js @@ -13,7 +13,7 @@ module.exports = () => { bot.onMembersAdded(async (context, next) => { const wasNonBotMemberAdded = context.activity.membersAdded.some( channelAccount => channelAccount.id !== context.activity.recipient.id); - + if (wasNonBotMemberAdded) { await context.sendActivity(`Hello! Your user ID is ${context.activity.from.id}`); } diff --git a/samples/01.getting-started/k.direct-line-token/web/src/index.js b/samples/01.getting-started/k.direct-line-token/web/src/index.js index 1858ea96d5..4ccd5ba24e 100644 --- a/samples/01.getting-started/k.direct-line-token/web/src/index.js +++ b/samples/01.getting-started/k.direct-line-token/web/src/index.js @@ -38,9 +38,8 @@ STATIC_FILES && }) ); - server.listen(PORT, () => { - STATIC_FILES && console.log(`Will serve static content from ${STATIC_FILES}`); - - console.log(`Rest API server is listening to port ${PORT}`); - }); - \ No newline at end of file +server.listen(PORT, () => { + STATIC_FILES && console.log(`Will serve static content from ${STATIC_FILES}`); + + console.log(`Rest API server is listening to port ${PORT}`); +}); From f8bf9c1cca70a7d63552b2fdbe013b5de34ef773 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 4 Mar 2021 10:00:01 -0800 Subject: [PATCH 04/14] Clarify FAQ in direct-line-token sample README --- samples/01.getting-started/k.direct-line-token/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md index 31777c2320..09f271e9fc 100644 --- a/samples/01.getting-started/k.direct-line-token/README.md +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -183,7 +183,7 @@ DIRECT_LINE_SECRET=a1b2c3.d4e5f6g7h8i9j0 ## What if I need a consistent user ID across sessions/devices? -Instead of randomly generating user IDs, the backend API could leverage a user's existing identity from a true identity provider. The user would first sign in to the site before talking to the bot. That way, if the user signs in using the same identity on a different browser or device, the user ID will be the same. This also prevents user impersonation because we can verify the user's identity with the identity provider. +Instead of randomly generating user IDs, the backend API could leverage a user's existing identity from a true identity provider. The user would first sign in to the site before talking to the bot. That way, if the user signed in using the same identity on a different browser or device, the user ID would be the same. This would also prevent user impersonation because we could verify the user's identity with the identity provider before issuing a Direct Line token. The flow could be: From a7d99acab998a4e407ad709587837295b8716ac3 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 4 Mar 2021 11:52:53 -0800 Subject: [PATCH 05/14] Add direct-line-token sample to samples README --- samples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/README.md b/samples/README.md index 98ab94e490..feef04225e 100644 --- a/samples/README.md +++ b/samples/README.md @@ -21,6 +21,7 @@ Here you can find all hosted samples of [Web Chat](https://github.com/microsoft/ | [`01.getting-started/h.minimal-markdown`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/01.getting-started/h.minimal-markdown) | Demonstrates how to add the CDN for Markdown-It dependency on top of the minimal bundle. | [Minimal with Markdown Demo](https://microsoft.github.io/BotFramework-WebChat/01.getting-started/h.minimal-markdown) | | [`01.getting-started/i.protocol-direct-line-app-service-extension`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/01.getting-started/i.protocol-direct-line-app-service-extension) | Demonstrates how to use Direct Line App Service Extension chat adapter | [Direct Line App Service Extension Demo](https://microsoft.github.io/BotFramework-WebChat/01.getting-started/i.protocol-direct-line-app-service-extension) | | [`01.getting-started/j.bundle-with-content-security-policy`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/01.getting-started/j.bundle-with-content-security-policy) | Demonstrates how to embed on a host with Content Security Policy configured. | [Content Security Policy Demo](https://microsoft.github.io/BotFramework-WebChat/01.getting-started/j.bundle-with-content-security-policy) | +| [`01.getting-started/k.direct-line-token`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/01.getting-started/k.direct-line-token) | Demonstrates how to use a Direct Line token instead of exposing the Direct Line secret. | | | **Branding, styling, and customization** | | | | [`02.branding-styling-and-customization/a.branding-web-chat`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/02.branding-styling-and-customization/a.branding-web-chat) | Introduces the ability to style Web Chat to match your brand. This method of custom styling will not break upon Web Chat updates. | [Branding Web Chat Demo](https://microsoft.github.io/BotFramework-WebChat/02.branding-styling-and-customization/a.branding-web-chat) | | [`02.branding-styling-and-customization/b.idiosyncratic-manual-styles`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/02.branding-styling-and-customization/b.idiosyncratic-manual-styles) | Demonstrates how to make manual style changes, and is a more complicated and time-consuming way to customize styling of Web Chat. Manual styles may be broken upon Web Chat updates. | [Idiosyncratic Styling Demo](https://microsoft.github.io/BotFramework-WebChat/02.branding-styling-and-customization/b.idiosyncratic-manual-styles) | From d54f697f08aca2494bf7e2b4e69bcbdf05a5b214 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 4 Mar 2021 11:58:30 -0800 Subject: [PATCH 06/14] Update CHANGELOG for direct-line-token sample --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a978c3371..38054ab0aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Samples - Fixes [#3632](https://github.com/microsoft/BotFramework-WebChat/issues/3632). Update reaction button sample : Add replyToId to the postActivity object, by [@amal-khalaf](https://github.com/amal-khalaf) in PR [#3769](https://github.com/microsoft/BotFramework-WebChat/pull/3769) +- Fixes [#2343](https://github.com/microsoft/BotFramework-WebChat/issues/2343). Add sample for Direct Line tokens, by [@navzam](https://github.com/navzam), in PR [#3779](https://github.com/microsoft/BotFramework-WebChat/pull/3779) ## [4.12.0] - 2021-02-18 From db2833802e1c2a30b8b1f36105e4f054349d80d3 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Mon, 8 Mar 2021 16:58:18 -0800 Subject: [PATCH 07/14] Move JS code into separate javascript folder --- .../{ => javascript}/bot/.gitignore | 0 .../{ => javascript}/bot/package-lock.json | 0 .../{ => javascript}/bot/package.json | 0 .../{ => javascript}/bot/src/createBot.js | 0 .../{ => javascript}/bot/src/createBotAdapter.js | 0 .../{ => javascript}/bot/src/index.js | 0 .../{ => javascript}/web/.gitignore | 0 .../{ => javascript}/web/package-lock.json | 0 .../{ => javascript}/web/package.json | 0 .../web/src/generateDirectLineToken.js | 0 .../{ => javascript}/web/src/index.js | 2 +- .../{ => javascript}/web/src/routes/botMessages.js | 0 .../web/src/routes/directLine/token.js | 0 .../{ => javascript}/web/src/utils/fetchJSON.js | 0 .../public/images/BotServices-Translucent.svg | 0 .../{web => }/public/images/BotServices.png | Bin .../k.direct-line-token/{web => }/public/index.html | 0 17 files changed, 1 insertion(+), 1 deletion(-) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/bot/.gitignore (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/bot/package-lock.json (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/bot/package.json (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/bot/src/createBot.js (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/bot/src/createBotAdapter.js (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/bot/src/index.js (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/.gitignore (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/package-lock.json (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/package.json (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/src/generateDirectLineToken.js (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/src/index.js (94%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/src/routes/botMessages.js (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/src/routes/directLine/token.js (100%) rename samples/01.getting-started/k.direct-line-token/{ => javascript}/web/src/utils/fetchJSON.js (100%) rename samples/01.getting-started/k.direct-line-token/{web => }/public/images/BotServices-Translucent.svg (100%) rename samples/01.getting-started/k.direct-line-token/{web => }/public/images/BotServices.png (100%) rename samples/01.getting-started/k.direct-line-token/{web => }/public/index.html (100%) diff --git a/samples/01.getting-started/k.direct-line-token/bot/.gitignore b/samples/01.getting-started/k.direct-line-token/javascript/bot/.gitignore similarity index 100% rename from samples/01.getting-started/k.direct-line-token/bot/.gitignore rename to samples/01.getting-started/k.direct-line-token/javascript/bot/.gitignore diff --git a/samples/01.getting-started/k.direct-line-token/bot/package-lock.json b/samples/01.getting-started/k.direct-line-token/javascript/bot/package-lock.json similarity index 100% rename from samples/01.getting-started/k.direct-line-token/bot/package-lock.json rename to samples/01.getting-started/k.direct-line-token/javascript/bot/package-lock.json diff --git a/samples/01.getting-started/k.direct-line-token/bot/package.json b/samples/01.getting-started/k.direct-line-token/javascript/bot/package.json similarity index 100% rename from samples/01.getting-started/k.direct-line-token/bot/package.json rename to samples/01.getting-started/k.direct-line-token/javascript/bot/package.json diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/createBot.js b/samples/01.getting-started/k.direct-line-token/javascript/bot/src/createBot.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/bot/src/createBot.js rename to samples/01.getting-started/k.direct-line-token/javascript/bot/src/createBot.js diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/createBotAdapter.js b/samples/01.getting-started/k.direct-line-token/javascript/bot/src/createBotAdapter.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/bot/src/createBotAdapter.js rename to samples/01.getting-started/k.direct-line-token/javascript/bot/src/createBotAdapter.js diff --git a/samples/01.getting-started/k.direct-line-token/bot/src/index.js b/samples/01.getting-started/k.direct-line-token/javascript/bot/src/index.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/bot/src/index.js rename to samples/01.getting-started/k.direct-line-token/javascript/bot/src/index.js diff --git a/samples/01.getting-started/k.direct-line-token/web/.gitignore b/samples/01.getting-started/k.direct-line-token/javascript/web/.gitignore similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/.gitignore rename to samples/01.getting-started/k.direct-line-token/javascript/web/.gitignore diff --git a/samples/01.getting-started/k.direct-line-token/web/package-lock.json b/samples/01.getting-started/k.direct-line-token/javascript/web/package-lock.json similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/package-lock.json rename to samples/01.getting-started/k.direct-line-token/javascript/web/package-lock.json diff --git a/samples/01.getting-started/k.direct-line-token/web/package.json b/samples/01.getting-started/k.direct-line-token/javascript/web/package.json similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/package.json rename to samples/01.getting-started/k.direct-line-token/javascript/web/package.json diff --git a/samples/01.getting-started/k.direct-line-token/web/src/generateDirectLineToken.js b/samples/01.getting-started/k.direct-line-token/javascript/web/src/generateDirectLineToken.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/src/generateDirectLineToken.js rename to samples/01.getting-started/k.direct-line-token/javascript/web/src/generateDirectLineToken.js diff --git a/samples/01.getting-started/k.direct-line-token/web/src/index.js b/samples/01.getting-started/k.direct-line-token/javascript/web/src/index.js similarity index 94% rename from samples/01.getting-started/k.direct-line-token/web/src/index.js rename to samples/01.getting-started/k.direct-line-token/javascript/web/src/index.js index 4ccd5ba24e..2d192715b0 100644 --- a/samples/01.getting-started/k.direct-line-token/web/src/index.js +++ b/samples/01.getting-started/k.direct-line-token/javascript/web/src/index.js @@ -34,7 +34,7 @@ STATIC_FILES && '/**/*', restify.plugins.serveStatic({ default: 'index.html', - directory: join(__dirname, '..', STATIC_FILES) + directory: join(__dirname, '..', '..', '..', STATIC_FILES) }) ); diff --git a/samples/01.getting-started/k.direct-line-token/web/src/routes/botMessages.js b/samples/01.getting-started/k.direct-line-token/javascript/web/src/routes/botMessages.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/src/routes/botMessages.js rename to samples/01.getting-started/k.direct-line-token/javascript/web/src/routes/botMessages.js diff --git a/samples/01.getting-started/k.direct-line-token/web/src/routes/directLine/token.js b/samples/01.getting-started/k.direct-line-token/javascript/web/src/routes/directLine/token.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/src/routes/directLine/token.js rename to samples/01.getting-started/k.direct-line-token/javascript/web/src/routes/directLine/token.js diff --git a/samples/01.getting-started/k.direct-line-token/web/src/utils/fetchJSON.js b/samples/01.getting-started/k.direct-line-token/javascript/web/src/utils/fetchJSON.js similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/src/utils/fetchJSON.js rename to samples/01.getting-started/k.direct-line-token/javascript/web/src/utils/fetchJSON.js diff --git a/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices-Translucent.svg b/samples/01.getting-started/k.direct-line-token/public/images/BotServices-Translucent.svg similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/public/images/BotServices-Translucent.svg rename to samples/01.getting-started/k.direct-line-token/public/images/BotServices-Translucent.svg diff --git a/samples/01.getting-started/k.direct-line-token/web/public/images/BotServices.png b/samples/01.getting-started/k.direct-line-token/public/images/BotServices.png similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/public/images/BotServices.png rename to samples/01.getting-started/k.direct-line-token/public/images/BotServices.png diff --git a/samples/01.getting-started/k.direct-line-token/web/public/index.html b/samples/01.getting-started/k.direct-line-token/public/index.html similarity index 100% rename from samples/01.getting-started/k.direct-line-token/web/public/index.html rename to samples/01.getting-started/k.direct-line-token/public/index.html From 6bd17c1e62e6c61386b7e8acb229d981e51be900 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Wed, 10 Mar 2021 14:59:59 -0800 Subject: [PATCH 08/14] Add C# bot for direct-line-token sample --- .../k.direct-line-token/csharp/bot/.gitignore | 2 + .../csharp/bot/AdapterWithErrorHandler.cs | 24 +++++++++++ .../csharp/bot/Bots/UserIDBot.cs | 28 +++++++++++++ .../csharp/bot/Controllers/BotController.cs | 27 +++++++++++++ .../k.direct-line-token/csharp/bot/Program.cs | 20 ++++++++++ .../csharp/bot/Properties/launchSettings.json | 27 +++++++++++++ .../k.direct-line-token/csharp/bot/Startup.cs | 40 +++++++++++++++++++ .../csharp/bot/TokenSampleBot.csproj | 11 +++++ .../csharp/bot/appsettings.json | 12 ++++++ 9 files changed, 191 insertions(+) create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/.gitignore create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/AdapterWithErrorHandler.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/Bots/UserIDBot.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/Controllers/BotController.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/Program.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/Properties/launchSettings.json create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/Startup.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/TokenSampleBot.csproj create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/bot/appsettings.json diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/.gitignore b/samples/01.getting-started/k.direct-line-token/csharp/bot/.gitignore new file mode 100644 index 0000000000..d45db4db8e --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/.gitignore @@ -0,0 +1,2 @@ +[Bb]in/ +[Oo]bj/ \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/AdapterWithErrorHandler.cs b/samples/01.getting-started/k.direct-line-token/csharp/bot/AdapterWithErrorHandler.cs new file mode 100644 index 0000000000..88fbb15a8a --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/AdapterWithErrorHandler.cs @@ -0,0 +1,24 @@ +using Microsoft.Bot.Builder.Integration.AspNet.Core; +using Microsoft.Bot.Builder.TraceExtensions; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace TokenSampleBot +{ + public class AdapterWithErrorHandler : BotFrameworkHttpAdapter + { + public AdapterWithErrorHandler(IConfiguration configuration, ILogger logger) + : base(configuration, logger) + { + OnTurnError = async (turnContext, exception) => + { + logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); + + await turnContext.SendActivityAsync("The bot encountered an error or bug."); + await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); + + await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); + }; + } + } +} \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/Bots/UserIDBot.cs b/samples/01.getting-started/k.direct-line-token/csharp/bot/Bots/UserIDBot.cs new file mode 100644 index 0000000000..06c295ba58 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/Bots/UserIDBot.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Schema; + +namespace TokenSampleBot.Bots +{ + public class UserIDBot : ActivityHandler + { + protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) + { + await turnContext.SendActivityAsync($"Your user ID is {turnContext.Activity.From.Id}"); + } + + protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) + { + bool wasNonBotMemberAdded = membersAdded.Any( + channelAccount => channelAccount.Id != turnContext.Activity.Recipient.Id); + + if (wasNonBotMemberAdded) + { + await turnContext.SendActivityAsync($"Hello! Your user ID is {turnContext.Activity.From.Id}"); + } + } + } +} \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/Controllers/BotController.cs b/samples/01.getting-started/k.direct-line-token/csharp/bot/Controllers/BotController.cs new file mode 100644 index 0000000000..e03fa98562 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/Controllers/BotController.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Integration.AspNet.Core; + +namespace TokenSampleBot.Controllers +{ + [Route("api/messages")] + [ApiController] + public class BotController : ControllerBase + { + private readonly IBotFrameworkHttpAdapter Adapter; + private readonly IBot Bot; + + public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) + { + Adapter = adapter; + Bot = bot; + } + + [HttpPost, HttpGet] + public async Task PostAsync() + { + await Adapter.ProcessAsync(Request, Response, Bot); + } + } +} \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/Program.cs b/samples/01.getting-started/k.direct-line-token/csharp/bot/Program.cs new file mode 100644 index 0000000000..2699381c14 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace TokenSampleBot +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/Properties/launchSettings.json b/samples/01.getting-started/k.direct-line-token/csharp/bot/Properties/launchSettings.json new file mode 100644 index 0000000000..287068ad3e --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:49493", + "sslPort": 44370 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Bot": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "https://localhost:3979;http://localhost:3978", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/Startup.cs b/samples/01.getting-started/k.direct-line-token/csharp/bot/Startup.cs new file mode 100644 index 0000000000..3dccd854a1 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/Startup.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Integration.AspNet.Core; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using TokenSampleBot.Bots; + +namespace TokenSampleBot +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + services.AddSingleton(); + + services.AddTransient(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/TokenSampleBot.csproj b/samples/01.getting-started/k.direct-line-token/csharp/bot/TokenSampleBot.csproj new file mode 100644 index 0000000000..f5e9e4aa73 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/TokenSampleBot.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + diff --git a/samples/01.getting-started/k.direct-line-token/csharp/bot/appsettings.json b/samples/01.getting-started/k.direct-line-token/csharp/bot/appsettings.json new file mode 100644 index 0000000000..addcd2f096 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/bot/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "MicrosoftAppId": "", + "MicrosoftAppPassword": "" +} From 64e6cb1df00720859fb4e9dcc0ba9f44cd25cbd7 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Wed, 10 Mar 2021 15:01:39 -0800 Subject: [PATCH 09/14] Add C# API for direct-line-token-sample --- .../k.direct-line-token/csharp/web/.gitignore | 2 + .../web/Controllers/DirectLineController.cs | 70 +++++++++++++++++++ .../k.direct-line-token/csharp/web/Program.cs | 20 ++++++ .../csharp/web/Properties/launchSettings.json | 28 ++++++++ .../k.direct-line-token/csharp/web/Startup.cs | 59 ++++++++++++++++ .../csharp/web/TokenSampleApi.csproj | 8 +++ .../csharp/web/appsettings.json | 11 +++ 7 files changed, 198 insertions(+) create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/.gitignore create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/Program.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/Properties/launchSettings.json create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/TokenSampleApi.csproj create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/appsettings.json diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/.gitignore b/samples/01.getting-started/k.direct-line-token/csharp/web/.gitignore new file mode 100644 index 0000000000..d45db4db8e --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/.gitignore @@ -0,0 +1,2 @@ +[Bb]in/ +[Oo]bj/ \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs new file mode 100644 index 0000000000..63c20dc87f --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs @@ -0,0 +1,70 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +namespace TokenSampleApi.Controllers +{ + [ApiController] + public class DirectLineController : ControllerBase + { + private readonly string _directLineSecret; + + private readonly IHttpClientFactory _httpClientFactory; + + public DirectLineController(IConfiguration configuration, IHttpClientFactory httpClientFactory) + { + _directLineSecret = configuration["DirectLineSecret"]; + _httpClientFactory = httpClientFactory; + } + + // Endpoint for generating a Direct Line token bound to a random user ID + [HttpGet] + [Route("/api/directLine/token")] + public async Task Get() + { + // Generate a random user ID to use for DirectLine token + var randomUserId = GenerateRandomUserId(); + + // Provide user ID in the request body to bind the user ID to the token + var tokenRequestBody = new { user = new { id = randomUserId } }; + var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://directline.botframework.com/v3/directline/tokens/generate") + { + Headers = + { + Authorization = new AuthenticationHeaderValue("Bearer", _directLineSecret), + }, + Content = new StringContent(JsonSerializer.Serialize(tokenRequestBody), Encoding.UTF8, MediaTypeNames.Application.Json), + }; + + // Call Direct Line API to generate a Direct Line token + var httpClient = _httpClientFactory.CreateClient(); + var tokenResponse = await httpClient.SendAsync(tokenRequest); + + if (!tokenResponse.IsSuccessStatusCode) + { + return this.BadRequest(); + } + + var tokenResponseString = await tokenResponse.Content.ReadAsStringAsync(); + return this.Content(tokenResponseString, MediaTypeNames.Application.Json); + } + + // Generates a random user ID + // Prefixed with "dl_", as required by the Direct Line API + private static string GenerateRandomUserId() + { + byte[] tokenData = new byte[16]; + using var rng = new RNGCryptoServiceProvider(); + rng.GetBytes(tokenData); + + return $"dl_{BitConverter.ToString(tokenData).Replace("-", "").ToLower()}"; + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Program.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Program.cs new file mode 100644 index 0000000000..725a9b1c4a --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace TokenSampleApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Properties/launchSettings.json b/samples/01.getting-started/k.direct-line-token/csharp/web/Properties/launchSettings.json new file mode 100644 index 0000000000..77cb8075de --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:19638", + "sslPort": 44354 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "TokenSampleApi": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs new file mode 100644 index 0000000000..a22467755b --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs @@ -0,0 +1,59 @@ +using System.IO; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; + +namespace TokenSampleApi +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + services.AddHttpClient(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + // We will use the REST API server to serve static web content to simplify deployment for demonstration purposes + IFileProvider staticFileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "..", "..", "public")); + app.UseDefaultFiles(new DefaultFilesOptions + { + FileProvider = staticFileProvider, + }); + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = staticFileProvider, + }); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/TokenSampleApi.csproj b/samples/01.getting-started/k.direct-line-token/csharp/web/TokenSampleApi.csproj new file mode 100644 index 0000000000..d12c450b7b --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/TokenSampleApi.csproj @@ -0,0 +1,8 @@ + + + + netcoreapp3.1 + + + + diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/appsettings.json b/samples/01.getting-started/k.direct-line-token/csharp/web/appsettings.json new file mode 100644 index 0000000000..bf4b27ff34 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "DirectLineSecret": "" +} From 7c781b25859e089b438ad66b0844179cb5d559ea Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Wed, 10 Mar 2021 16:36:49 -0800 Subject: [PATCH 10/14] Update direct-line-token sample README for C# --- .../k.direct-line-token/README.md | 141 +++++++++++++++++- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md index 09f271e9fc..4eee773852 100644 --- a/samples/01.getting-started/k.direct-line-token/README.md +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -24,14 +24,27 @@ This demo includes a bot that you will run locally, so before running the code, To host this demo, you will need to clone the code and run locally. +
JavaScript + 1. Clone this repository 1. Create two empty files for environment variables, `/bot/.env` and `/web/.env` +
+ +
C# + +1. Clone this repository +1. Open the two `appsettings.json` files at `/bot/appsettings.json` and `/web/appsettings.json` + +
+ ## Setup Azure Bot Services > We prefer to use [Bot Channel Registration](https://ms.portal.azure.com/#create/Microsoft.BotServiceConnectivityGalleryPackage) during development. This will help you diagnose problems locally without deploying to the server and speed up development. -You can follow our instructions on how to [setup a new Bot Channel Registration](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-3.0). +You can follow our instructions on how to [setup a new Bot Channel Registration](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-3.0). Then save the resulting IDs/secrets into the appropriate local environment files, depending on your language: + +
JavaScript 1. Save the Microsoft App ID and password to `/bot/.env` - `MICROSOFT_APP_ID=12345678-1234-5678-abcd-12345678abcd` @@ -39,6 +52,18 @@ You can follow our instructions on how to [setup a new Bot Channel Registration] 1. Save the Web Chat secret to `/web/.env` - `DIRECT_LINE_SECRET=a1b2c3.d4e5f6g7h8i9j0` +
+ +
C# + +1. Save the Microsoft App ID and password to `/bot/appsettings.json` + - `"MicrosoftAppId": "12345678-1234-5678-abcd-12345678abcd"` + - `"MicrosoftAppPassword": "a1b2c3d4e5f6"` +1. Save the Web Chat secret to `/web/appsettings.json` + - `"DirectLineSecret": "a1b2c3.d4e5f6g7h8i9j0"` + +
+ During development, you will run your bot locally. Azure Bot Services will send activities to your bot thru a public URL. You can use [ngrok](https://ngrok.com/) to expose your bot server on a public URL. 1. Run `ngrok http -host-header=localhost:3978 3978` @@ -52,9 +77,22 @@ During development, you will run your bot locally. Azure Bot Services will send ## Prepare and run the code -1. Under each of `bot`, and `web` folder, run the following - 1. `npm install` - 1. `npm start` +1. Under each of `bot`, and `web` folder, run the following commands, depending on your language: + +
JavaScript + + 1. `npm install` + 1. `npm start` + +
+ +
C# + + 1. `dotnet build` + 1. `dotnet run` + +
+ 1. Browse to http://localhost:5000/ to start the demo # Things to try out @@ -76,6 +114,8 @@ The code is organized into two separate folders: In this sample, the user is anonymous, so the API randomly generates a user ID: +
JavaScript + ```js // web/src/routes/directLine/token.js @@ -85,12 +125,33 @@ async function generateRandomUserId() { } ``` +
+ +
C# + +```csharp +// web/Controllers/DirectLineController.cs + +private static string GenerateRandomUserId() +{ + byte[] tokenData = new byte[16]; + using var rng = new RNGCryptoServiceProvider(); + rng.GetBytes(tokenData); + + return $"dl_{BitConverter.ToString(tokenData).Replace("-", "").ToLower()}"; +} +``` + +
+ The user ID is prefixed with "dl_" as required by the [Direct Line token API](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0#generate-token). ## Retrieving a user-specific Direct Line token The backend API calls the Direct Line API to retrieve a Direct Line token. Notice that we pass the user ID in the body of the request: +
JavaScript + ```js // web/src/generateDirectLineToken.js @@ -112,6 +173,29 @@ async function generateDirectLineToken(secret, userId) { }; ``` +
+ +
C# + +```csharp +// web/Controllers/DirectLineController.cs + +var tokenRequestBody = new { user = new { id = randomUserId } }; +var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://directline.botframework.com/v3/directline/tokens/generate") +{ + Headers = + { + Authorization = new AuthenticationHeaderValue("Bearer", _directLineSecret), + }, + Content = new StringContent(JsonSerializer.Serialize(tokenRequestBody), Encoding.UTF8, MediaTypeNames.Application.Json), +}; + +var httpClient = _httpClientFactory.CreateClient(); +var tokenResponse = await httpClient.SendAsync(tokenRequest); +``` + +
+ The resulting Direct Line token will be bound to the passed user ID. ## Calling the API and rendering Web Chat @@ -119,7 +203,7 @@ The resulting Direct Line token will be bound to the passed user ID. The UI calls the API and uses the resulting Direct Line token to render Web Chat: ```js -// web/public/index.html +// public/index.html const { token } = await fetchJSON('/api/directline/token'); @@ -160,11 +244,13 @@ To avoid impersonation, the recommended approach is for the server to bind a use To keep things simple, this sample generates a random user ID on the server-side and binds it to the Direct Line token. While this mitigates impersonation concerns, the downside is that users will have a different ID every time they talk to the bot. -## Content of the `.env` files +## Content of the local environment files + +The `.env` / `appsettings.json` files hold the environment variable critical to run the service. These are usually security-sensitive information and must not be committed to version control. Although we recommend to keep them in [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/), for simplicity of this sample, we would keep them in local environment files. -The `.env` file hold the environment variable critical to run the service. These are usually security-sensitive information and must not be committed to version control. Although we recommend to keep them in [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/), for simplicity of this sample, we would keep them in `.env` files. +To ease the setup of this sample, here is the template of the local environment files for each language. -To ease the setup of this sample, here is the template of `.env` files. +
JavaScript ### `/bot/.env` @@ -179,6 +265,45 @@ MICROSOFT_APP_PASSWORD=a1b2c3d4e5f6 DIRECT_LINE_SECRET=a1b2c3.d4e5f6g7h8i9j0 ``` +
+ +
C# + +### `/bot/appsettings.json` + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "MicrosoftAppId": "12345678-1234-5678-abcd-12345678abcd", + "MicrosoftAppPassword": "a1b2c3d4e5f6" +} +``` + +### `/web/appsettings.json` + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "DirectLineSecret": "a1b2c3.d4e5f6g7h8i9j0" +} +``` + +
+ # Frequently asked questions ## What if I need a consistent user ID across sessions/devices? From 4e266b3f3a2815205f7198b2659d83f1370f9d4a Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Wed, 10 Mar 2021 16:39:38 -0800 Subject: [PATCH 11/14] Clarify README in direct-line-token sample --- samples/01.getting-started/k.direct-line-token/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md index 4eee773852..4c5db267b7 100644 --- a/samples/01.getting-started/k.direct-line-token/README.md +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -200,7 +200,7 @@ The resulting Direct Line token will be bound to the passed user ID. ## Calling the API and rendering Web Chat -The UI calls the API and uses the resulting Direct Line token to render Web Chat: +The client-side page calls the API and uses the resulting Direct Line token to render Web Chat: ```js // public/index.html From 5c0dc9aed2d7aae7705a0a1e5331ebce5c65b47a Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 11 Mar 2021 10:44:26 -0800 Subject: [PATCH 12/14] In dl-token sample, separate dl API code --- .../k.direct-line-token/README.md | 38 ++++++++--- .../web/Controllers/DirectLineController.cs | 42 ++++-------- .../web/Models/DirectLineTokenDetails.cs | 11 +++ .../csharp/web/Services/DirectLineService.cs | 68 +++++++++++++++++++ .../k.direct-line-token/csharp/web/Startup.cs | 3 +- 5 files changed, 123 insertions(+), 39 deletions(-) create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/Models/DirectLineTokenDetails.cs create mode 100644 samples/01.getting-started/k.direct-line-token/csharp/web/Services/DirectLineService.cs diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md index 4c5db267b7..1b792c9f93 100644 --- a/samples/01.getting-started/k.direct-line-token/README.md +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -178,20 +178,38 @@ async function generateDirectLineToken(secret, userId) {
C# ```csharp -// web/Controllers/DirectLineController.cs +// web/Services/DirectLineService.cs + +httpClient.BaseAddress = new Uri("https://directline.botframework.com/"); + +... -var tokenRequestBody = new { user = new { id = randomUserId } }; -var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://directline.botframework.com/v3/directline/tokens/generate") +public async Task GetTokenAsync(string directLineSecret, string userId, CancellationToken cancellationToken = default) { - Headers = + var tokenRequestBody = new { user = new { id = userId } }; + var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "v3/directline/tokens/generate") { - Authorization = new AuthenticationHeaderValue("Bearer", _directLineSecret), - }, - Content = new StringContent(JsonSerializer.Serialize(tokenRequestBody), Encoding.UTF8, MediaTypeNames.Application.Json), -}; + Headers = + { + { "Authorization", $"Bearer {directLineSecret}" }, + }, + Content = new StringContent(JsonSerializer.Serialize(tokenRequestBody), Encoding.UTF8, MediaTypeNames.Application.Json), + }; -var httpClient = _httpClientFactory.CreateClient(); -var tokenResponse = await httpClient.SendAsync(tokenRequest); + var tokenResponseMessage = await _httpClient.SendAsync(tokenRequest, cancellationToken); + + ... + + using var responseContentStream = await tokenResponseMessage.Content.ReadAsStreamAsync(); + var tokenResponse = await JsonSerializer.DeserializeAsync(responseContentStream); + + return new DirectLineTokenDetails + { + Token = tokenResponse.Token, + ConversationId = tokenResponse.ConversationId, + ExpiresIn = tokenResponse.ExpiresIn, + }; +} ```
diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs index 63c20dc87f..dbf218a5ea 100644 --- a/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Controllers/DirectLineController.cs @@ -1,27 +1,24 @@ using System; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Mime; using System.Security.Cryptography; -using System.Text; -using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; +using TokenSampleApi.Models; +using TokenSampleApi.Services; namespace TokenSampleApi.Controllers { [ApiController] public class DirectLineController : ControllerBase { - private readonly string _directLineSecret; + private readonly DirectLineService _directLineService; - private readonly IHttpClientFactory _httpClientFactory; + private readonly string _directLineSecret; - public DirectLineController(IConfiguration configuration, IHttpClientFactory httpClientFactory) + public DirectLineController(DirectLineService directLineService, IConfiguration configuration) { + _directLineService = directLineService; _directLineSecret = configuration["DirectLineSecret"]; - _httpClientFactory = httpClientFactory; } // Endpoint for generating a Direct Line token bound to a random user ID @@ -32,28 +29,17 @@ public async Task Get() // Generate a random user ID to use for DirectLine token var randomUserId = GenerateRandomUserId(); - // Provide user ID in the request body to bind the user ID to the token - var tokenRequestBody = new { user = new { id = randomUserId } }; - var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://directline.botframework.com/v3/directline/tokens/generate") + DirectLineTokenDetails directLineTokenDetails; + try { - Headers = - { - Authorization = new AuthenticationHeaderValue("Bearer", _directLineSecret), - }, - Content = new StringContent(JsonSerializer.Serialize(tokenRequestBody), Encoding.UTF8, MediaTypeNames.Application.Json), - }; - - // Call Direct Line API to generate a Direct Line token - var httpClient = _httpClientFactory.CreateClient(); - var tokenResponse = await httpClient.SendAsync(tokenRequest); - - if (!tokenResponse.IsSuccessStatusCode) + directLineTokenDetails = await _directLineService.GetTokenAsync(_directLineSecret, randomUserId); + } + catch (InvalidOperationException invalidOpException) { - return this.BadRequest(); + return BadRequest(new { message = invalidOpException.Message }); } - - var tokenResponseString = await tokenResponse.Content.ReadAsStringAsync(); - return this.Content(tokenResponseString, MediaTypeNames.Application.Json); + + return this.Ok(new { token = directLineTokenDetails.Token }); } // Generates a random user ID diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Models/DirectLineTokenDetails.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Models/DirectLineTokenDetails.cs new file mode 100644 index 0000000000..3596b76ca8 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Models/DirectLineTokenDetails.cs @@ -0,0 +1,11 @@ +namespace TokenSampleApi.Models +{ + public class DirectLineTokenDetails + { + public string Token { get; set; } + + public int ExpiresIn { get; set; } + + public string ConversationId { get; set; } + } +} \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Services/DirectLineService.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Services/DirectLineService.cs new file mode 100644 index 0000000000..76c5b87ab0 --- /dev/null +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Services/DirectLineService.cs @@ -0,0 +1,68 @@ +using System; +using System.Net.Http; +using System.Net.Mime; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using TokenSampleApi.Models; + +namespace TokenSampleApi.Services +{ + public class DirectLineService + { + private readonly HttpClient _httpClient; + + public DirectLineService(HttpClient httpClient) + { + httpClient.BaseAddress = new Uri("https://directline.botframework.com/"); + + _httpClient = httpClient; + } + + // Generates a new Direct Line token given the secret. + // Provides user ID in the request body to bind the user ID to the token. + public async Task GetTokenAsync(string directLineSecret, string userId, CancellationToken cancellationToken = default) + { + var tokenRequestBody = new { user = new { id = userId } }; + var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "v3/directline/tokens/generate") + { + Headers = + { + { "Authorization", $"Bearer {directLineSecret}" }, + }, + Content = new StringContent(JsonSerializer.Serialize(tokenRequestBody), Encoding.UTF8, MediaTypeNames.Application.Json), + }; + + var tokenResponseMessage = await _httpClient.SendAsync(tokenRequest, cancellationToken); + + if (!tokenResponseMessage.IsSuccessStatusCode) + { + throw new InvalidOperationException($"Direct Line token API call failed with status code {tokenResponseMessage.StatusCode}"); + } + + using var responseContentStream = await tokenResponseMessage.Content.ReadAsStreamAsync(); + var tokenResponse = await JsonSerializer.DeserializeAsync(responseContentStream); + + return new DirectLineTokenDetails + { + Token = tokenResponse.Token, + ConversationId = tokenResponse.ConversationId, + ExpiresIn = tokenResponse.ExpiresIn, + }; + } + + private class DirectLineTokenApiResponse + { + [JsonPropertyName("token")] + public string Token { get; set; } + + [JsonPropertyName("expires_in")] + public int ExpiresIn { get; set; } + + [JsonPropertyName("conversationId")] + public string ConversationId { get; set; } + } + } +} \ No newline at end of file diff --git a/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs b/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs index a22467755b..c966d50a1b 100644 --- a/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs +++ b/samples/01.getting-started/k.direct-line-token/csharp/web/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using TokenSampleApi.Services; namespace TokenSampleApi { @@ -22,7 +23,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - services.AddHttpClient(); + services.AddHttpClient(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. From ce8d533df7ee0df21aae00e753441eae277c5343 Mon Sep 17 00:00:00 2001 From: Nafis Zaman Date: Thu, 11 Mar 2021 10:51:20 -0800 Subject: [PATCH 13/14] Remove CI badge from top of dl-token sample README --- samples/01.getting-started/k.direct-line-token/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md index 1b792c9f93..ae933c2467 100644 --- a/samples/01.getting-started/k.direct-line-token/README.md +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -1,7 +1,5 @@ # Direct Line Token Sample -![samples/01.k.direct-line-token](https://github.com/microsoft/BotFramework-WebChat/workflows/samples/01.k.direct-line-token/badge.svg) - # Description This sample demonstrates how to integrate Web Chat in a way that 1) does not expose your Direct Line secret to the browser, and 2) mitigates user impersonation by not allowing the client to set its own user ID. From 7c5d31698c817d450536ac8da6a488e45a880734 Mon Sep 17 00:00:00 2001 From: Corina <14900841+corinagum@users.noreply.github.com> Date: Thu, 22 Apr 2021 15:50:16 -0700 Subject: [PATCH 14/14] Apply suggestions from code review --- samples/01.getting-started/k.direct-line-token/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/01.getting-started/k.direct-line-token/README.md b/samples/01.getting-started/k.direct-line-token/README.md index ae933c2467..03f14dbda8 100644 --- a/samples/01.getting-started/k.direct-line-token/README.md +++ b/samples/01.getting-started/k.direct-line-token/README.md @@ -22,14 +22,14 @@ This demo includes a bot that you will run locally, so before running the code, To host this demo, you will need to clone the code and run locally. -
JavaScript +
Clone the JavaScript project 1. Clone this repository 1. Create two empty files for environment variables, `/bot/.env` and `/web/.env`
-
C# +
Clone the C# project 1. Clone this repository 1. Open the two `appsettings.json` files at `/bot/appsettings.json` and `/web/appsettings.json`