[next] Auth Callback + Chunking (#2139)

* feat: added new auth-callback

* fix: removed unused polyfill code

* fix: fixed missed intersection observer

* feat: enabled vendor chunks

* fix: fix some issues with chunk splitting

* fix: added intersection-observer to app polyfill

* fix: fixed test

* fix: removed lodash plugin which caused issue in prod

* chore: access_token -> accessToken

* feat: Show social login errors

* fix: lint + add test

* fix: restore width after facebook social login
This commit is contained in:
Wyatt Johnson
2019-01-11 14:31:24 +00:00
committed by Kiwi
parent 0e941222c5
commit 94eb72a9bf
42 changed files with 880 additions and 277 deletions
+4 -1
View File
@@ -60,7 +60,10 @@ export default function({
overlay: false,
historyApiFallback: {
disableDotRule: true,
rewrites: [{ from: /^\/admin/, to: "/admin.html" }],
rewrites: [
{ from: /^\/admin/, to: "/admin.html" },
{ from: /^\/embed\/auth\/callback/, to: "/auth-callback.html" },
],
},
public: allowedHost,
index: "embed.html",
+215 -115
View File
@@ -2197,6 +2197,12 @@
"@types/enzyme": "*"
}
},
"@types/escape-string-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/escape-string-regexp/-/escape-string-regexp-1.0.0.tgz",
"integrity": "sha512-KAruqgk9/340M4MYYasdBET+lyYN8KMXUuRKWO72f4SbmIMMFp9nnJiXUkJS0HC2SFe4x0R/fLozXIzqoUfSjA==",
"dev": true
},
"@types/eventemitter2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@types/eventemitter2/-/eventemitter2-4.1.0.tgz",
@@ -2281,9 +2287,9 @@
"dev": true
},
"@types/html-webpack-plugin": {
"version": "2.30.4",
"resolved": "https://registry.npmjs.org/@types/html-webpack-plugin/-/html-webpack-plugin-2.30.4.tgz",
"integrity": "sha512-jq9jZdJAsJPBWitnlUPeWYJ01Aaekiq0/7bAihZ08lnBqTHVUvR+uuG+3FuUjZ2FCQR7PsmmOJpFF6/DD2Ngtg==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@types/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha512-in9rViBsTRB4ZApndZ12It68nGzSMHVK30JD7c49iLIHMFeTPbP7I7wevzMv7re2o0k5TlU6Ry/beyrmgWX7Bg==",
"dev": true,
"requires": {
"@types/html-minifier": "*",
@@ -2374,15 +2380,6 @@
"integrity": "sha512-iiJbKLZbhSa6FYRip/9ZDX6HXhayXLDGY2Fqws9cOkEQ6XeKfaxB0sC541mowZJueYyMnVUmmG+al5/4fCDrgw==",
"dev": true
},
"@types/lodash-webpack-plugin": {
"version": "0.11.3",
"resolved": "http://registry.npmjs.org/@types/lodash-webpack-plugin/-/lodash-webpack-plugin-0.11.3.tgz",
"integrity": "sha512-zzwcefaw1wAQZsSWa1ChfhgjtoOTVdzoxSnmuRrGlHnAEF2k+NIAzgJlUc2V2RsvlOaE2WT/CM/zW9m+GX8wAw==",
"dev": true,
"requires": {
"@types/webpack": "*"
}
},
"@types/long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz",
@@ -5959,9 +5956,9 @@
},
"dependencies": {
"array-flatten": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz",
"integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
"dev": true
}
}
@@ -7146,7 +7143,7 @@
},
"colors": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
@@ -7954,7 +7951,7 @@
},
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
"resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
"dev": true
},
@@ -9703,6 +9700,35 @@
"locate-path": "^3.0.0"
}
},
"html-webpack-plugin": {
"version": "3.2.0",
"resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
"dev": true,
"requires": {
"html-minifier": "^3.2.3",
"loader-utils": "^0.2.16",
"lodash": "^4.17.3",
"pretty-error": "^2.0.2",
"tapable": "^1.0.0",
"toposort": "^1.0.0",
"util.promisify": "1.0.0"
},
"dependencies": {
"loader-utils": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
"integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
"dev": true,
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0",
"object-assign": "^4.0.1"
}
}
}
},
"json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
@@ -10005,20 +10031,12 @@
}
},
"dom-converter": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz",
"integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
"integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
"dev": true,
"requires": {
"utila": "~0.3"
},
"dependencies": {
"utila": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
"integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=",
"dev": true
}
"utila": "~0.4"
}
},
"dom-helpers": {
@@ -13075,9 +13093,9 @@
}
},
"handle-thing": {
"version": "1.2.5",
"resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
"integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
"integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==",
"dev": true
},
"handlebars": {
@@ -13448,37 +13466,72 @@
}
},
"html-webpack-plugin": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz",
"integrity": "sha512-y5l4lGxOW3pz3xBTFdfB9rnnrWRPVxlAhX6nrBYIcW+2k2zC3mSp/3DxlWVCMBfnO6UAnoF8OcFn0IMy6kaKAQ==",
"dev": true,
"requires": {
"html-minifier": "^3.2.3",
"loader-utils": "^0.2.16",
"lodash": "^4.17.3",
"pretty-error": "^2.0.2",
"tapable": "^1.0.0",
"toposort": "^1.0.0",
"html-minifier": "^3.5.20",
"loader-utils": "^1.1.0",
"lodash": "^4.17.11",
"pretty-error": "^2.1.1",
"tapable": "^1.1.0",
"util.promisify": "1.0.0"
},
"dependencies": {
"json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
},
"loader-utils": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
"integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
"dev": true,
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0",
"object-assign": "^4.0.1"
"source-map": "~0.6.0"
}
},
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"dev": true
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"html-minifier": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
"integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
"dev": true,
"requires": {
"camel-case": "3.0.x",
"clean-css": "4.2.x",
"commander": "2.17.x",
"he": "1.2.x",
"param-case": "2.1.x",
"relateurl": "0.2.x",
"uglify-js": "3.4.x"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"tapable": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
"integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
"dev": true
}
}
},
@@ -17123,15 +17176,6 @@
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz",
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q=="
},
"lodash-webpack-plugin": {
"version": "0.11.5",
"resolved": "https://registry.npmjs.org/lodash-webpack-plugin/-/lodash-webpack-plugin-0.11.5.tgz",
"integrity": "sha512-QWfEIYxpixOdbd6KBe5g6MDWcyTgP3trDXwKHFqTlXrWiLcs/67fGQ0IWeRyhWlTITQIgMpJAYd2oeIztuV5VA==",
"dev": true,
"requires": {
"lodash": "^4.17.4"
}
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -19517,9 +19561,9 @@
"dev": true
},
"portfinder": {
"version": "1.0.19",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.19.tgz",
"integrity": "sha512-23aeQKW9KgHe6citUrG3r9HjeX6vls0h713TAa+CwTKZwNIr/pD2ApaxYF4Um3ZZyq4ar+Siv3+fhoHaIwSOSw==",
"version": "1.0.20",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
"integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==",
"dev": true,
"requires": {
"async": "^1.5.2",
@@ -19529,7 +19573,7 @@
"dependencies": {
"async": {
"version": "1.5.2",
"resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
"dev": true
}
@@ -24163,24 +24207,16 @@
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
},
"renderkid": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz",
"integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz",
"integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==",
"dev": true,
"requires": {
"css-select": "^1.1.0",
"dom-converter": "~0.1",
"dom-converter": "~0.2",
"htmlparser2": "~3.3.0",
"strip-ansi": "^3.0.0",
"utila": "~0.3"
},
"dependencies": {
"utila": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
"integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=",
"dev": true
}
"utila": "^0.4.0"
}
},
"repeat-element": {
@@ -25211,32 +25247,69 @@
"dev": true
},
"spdy": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz",
"integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz",
"integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"handle-thing": "^1.2.5",
"debug": "^4.1.0",
"handle-thing": "^2.0.0",
"http-deceiver": "^1.2.7",
"safe-buffer": "^5.0.1",
"select-hose": "^2.0.0",
"spdy-transport": "^2.0.18"
"spdy-transport": "^3.0.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
}
},
"spdy-transport": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.1.tgz",
"integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
"integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"detect-node": "^2.0.3",
"debug": "^4.1.0",
"detect-node": "^2.0.4",
"hpack.js": "^2.1.6",
"obuf": "^1.1.1",
"readable-stream": "^2.2.9",
"safe-buffer": "^5.0.1",
"wbuf": "^1.7.2"
"obuf": "^1.1.2",
"readable-stream": "^3.0.6",
"wbuf": "^1.7.3"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"detect-node": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
"readable-stream": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
"integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"split": {
@@ -28305,9 +28378,9 @@
}
},
"webpack-dev-server": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz",
"integrity": "sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww==",
"version": "3.1.14",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz",
"integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==",
"dev": true,
"requires": {
"ansi-html": "0.0.7",
@@ -28329,21 +28402,23 @@
"portfinder": "^1.0.9",
"schema-utils": "^1.0.0",
"selfsigned": "^1.9.1",
"semver": "^5.6.0",
"serve-index": "^1.7.2",
"sockjs": "0.3.19",
"sockjs-client": "1.3.0",
"spdy": "^3.4.1",
"spdy": "^4.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^5.1.0",
"url": "^0.11.0",
"webpack-dev-middleware": "3.4.0",
"webpack-log": "^2.0.0",
"yargs": "12.0.2"
},
"dependencies": {
"ansi-colors": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.1.tgz",
"integrity": "sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ==",
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
"integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
"dev": true
},
"debug": {
@@ -28374,13 +28449,13 @@
}
},
"execa": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
"integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^3.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
@@ -28397,6 +28472,15 @@
"locate-path": "^3.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -28450,20 +28534,20 @@
"dev": true
},
"os-locale": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz",
"integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"execa": "^0.10.0",
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"p-limit": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz",
"integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
"integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
@@ -28493,6 +28577,16 @@
"find-up": "^3.0.0"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -28504,6 +28598,12 @@
"ajv-keywords": "^3.1.0"
}
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"dev": true
},
"sockjs-client": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz",
+4 -5
View File
@@ -135,12 +135,13 @@
"@types/dotenv": "^4.0.3",
"@types/enzyme": "^3.1.11",
"@types/enzyme-adapter-react-16": "^1.0.2",
"@types/escape-string-regexp": "^1.0.0",
"@types/eventemitter2": "^4.1.0",
"@types/express": "^4.16.0",
"@types/fs-extra": "^5.0.4",
"@types/graphql": "^0.13.3",
"@types/html-to-text": "^1.4.31",
"@types/html-webpack-plugin": "^2.30.4",
"@types/html-webpack-plugin": "^3.2.0",
"@types/ioredis": "^3.2.12",
"@types/jest": "^23.1.5",
"@types/joi": "^13.0.8",
@@ -149,7 +150,6 @@
"@types/linkify-it": "^2.0.4",
"@types/linkifyjs": "^2.1.0",
"@types/lodash": "^4.14.118",
"@types/lodash-webpack-plugin": "^0.11.3",
"@types/luxon": "^0.5.3",
"@types/mini-css-extract-plugin": "^0.2.0",
"@types/mongodb": "^3.1.14",
@@ -222,7 +222,7 @@
"gulp-cli": "^2.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-typescript": "^5.0.0-alpha.3",
"html-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^4.0.0-beta.5",
"husky": "^1.1.0",
"intersection-observer": "^0.5.1",
"jest": "^23.4.1",
@@ -233,7 +233,6 @@
"lint-staged": "^7.3.0",
"loader-utils": "^1.1.0",
"lodash-es": "^4.17.11",
"lodash-webpack-plugin": "^0.11.5",
"material-design-icons": "^3.0.1",
"mini-css-extract-plugin": "^0.5.0",
"npm-run-all": "^4.1.3",
@@ -294,7 +293,7 @@
"webpack": "^4.27.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10",
"webpack-dev-server": "^3.1.14",
"webpack-hot-client": "^4.1.1",
"webpack-manifest-plugin": "^2.0.4",
"whatwg-fetch": "^2.0.4"
+7
View File
@@ -42,6 +42,13 @@ const config = convict({
env: "WEBPACK_DISABLE_MINIMIZE",
arg: "disableMinimize",
},
disableChunkSplitting: {
doc: "Disables chunk splitting beheviour",
format: Boolean,
default: false,
env: "WEBPACK_DISABLE_CHUNK_SPLITTING",
arg: "disableChunkSplitting",
},
enableTreeShake: {
doc: "Enabled tree shaking in development",
format: Boolean,
+21 -3
View File
@@ -3,10 +3,8 @@ import CaseSensitivePathsPlugin from "case-sensitive-paths-webpack-plugin";
import CompressionPlugin from "compression-webpack-plugin";
import HtmlWebpackPlugin, { Options } from "html-webpack-plugin";
import { identity } from "lodash";
import LodashModuleReplacementPlugin from "lodash-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import path from "path";
import InterpolateHtmlPlugin from "react-dev-utils/InterpolateHtmlPlugin";
import WatchMissingNodeModulesPlugin from "react-dev-utils/WatchMissingNodeModulesPlugin";
import TerserPlugin from "terser-webpack-plugin";
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
@@ -17,6 +15,7 @@ import ManifestPlugin from "webpack-manifest-plugin";
import { Config } from "./config";
import { createClientEnv } from "./config";
import paths from "./paths";
import InterpolateHtmlPlugin from "./plugins/InterpolateHtmlPlugin";
import PublicURIWebpackPlugin from "./plugins/PublicURIWebpackPlugin";
/**
@@ -155,6 +154,9 @@ export default function createWebpackConfig(
// We can't use side effects because it disturbs css order
// https://github.com/webpack/webpack/issues/7094.
sideEffects: false,
splitChunks: {
chunks: config.get("disableChunkSplitting") ? "async" : "all",
},
minimize: minimize || treeShake,
minimizer: [
// Minify the code.
@@ -442,7 +444,6 @@ export default function createWebpackConfig(
],
},
plugins: [
new LodashModuleReplacementPlugin(),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
new webpack.DefinePlugin(envStringified),
@@ -509,6 +510,11 @@ export default function createWebpackConfig(
paths.appAuthIndex,
// Remove deactivated entries.
],
authCallback: [
...ifProduction(paths.appPublicPath),
...devServerEntries,
paths.appAuthCallbackIndex,
],
install: [
// We ship polyfills by default
paths.appPolyfill,
@@ -542,6 +548,14 @@ export default function createWebpackConfig(
inject: "body",
...htmlWebpackConfig,
}),
// Generates an `auth-callback.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "auth-callback.html",
template: paths.appAuthCallbackHTML,
chunks: ["authCallback"],
inject: "body",
...htmlWebpackConfig,
}),
// Generates an `install.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "install.html",
@@ -585,6 +599,10 @@ export default function createWebpackConfig(
...baseConfig,
optimization: {
...baseConfig.optimization,
// Ensure that we never split the embed into chunks.
splitChunks: {
chunks: "async",
},
// We can turn on sideEffects here as we don't use
// css here and don't run into: https://github.com/webpack/webpack/issues/7094
sideEffects: true,
+3
View File
@@ -30,6 +30,9 @@ export default {
appAuthLocalesTemplate: resolveSrc("core/client/auth/locales.ts"),
appAuthIndex: resolveSrc("core/client/auth/index.tsx"),
appAuthCallbackHTML: resolveSrc("core/client/auth-callback/index.html"),
appAuthCallbackIndex: resolveSrc("core/client/auth-callback/index.ts"),
appInstallHTML: resolveSrc("core/client/install/index.html"),
appInstallLocalesTemplate: resolveSrc("core/client/install/locales.ts"),
appInstallIndex: resolveSrc("core/client/install/index.tsx"),
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// This Webpack plugin lets us interpolate custom variables into `index.html`.
// Usage: `new InterpolateHtmlPlugin({ 'MY_VARIABLE': 42 })`
// Then, you can use %MY_VARIABLE% in your `index.html`.
// It works in tandem with HtmlWebpackPlugin.
// Learn more about creating plugins like this:
// https://github.com/ampedandwired/html-webpack-plugin#events
import escapeStringRegexp from "escape-string-regexp";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { Compiler, Plugin } from "webpack";
export default class InterpolateHtmlPlugin implements Plugin {
private replacements: Record<string, string>;
constructor(replacements: Record<string, string>) {
this.replacements = replacements;
}
public apply(compiler: Compiler) {
// The following was modified from the original source:
// https://github.com/jussikinnula/react-dev-utils/blob/9c290281877c026774c909b2900000c653f431a4/InterpolateHtmlPlugin.js
// to support Webpack 4.
compiler.hooks.compilation.tap("InterpolateHtmlPlugin", compilation => {
const hooks = (HtmlWebpackPlugin as any).getHooks(compilation);
hooks.afterTemplateExecution.tap("InterpolateHtmlPlugin", (data: any) => {
// Run HTML through a series of user-specified string replacements.
Object.keys(this.replacements).forEach(key => {
const value = this.replacements[key];
data.html = data.html.replace(
new RegExp("%" + escapeStringRegexp(key) + "%", "g"),
value
);
});
});
});
}
}
@@ -1,6 +1,38 @@
import { Hooks } from "html-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { AsyncSeriesWaterfallHook } from "tapable";
import { Compiler, Plugin } from "webpack";
// Copied from @types/html-webpack-plugin
interface HtmlTagObject {
/**
* Attributes of the html tag
* E.g. `{'disabled': true, 'value': 'demo'}`
*/
attributes: {
[attributeName: string]: string | boolean;
};
/**
* Wether this html must not contain innerHTML
* @see https://www.w3.org/TR/html5/syntax.html#void-elements
*/
voidTag: boolean;
/**
* The tag name e.g. `'div'`
*/
tagName: string;
/**
* Inner HTML The
*/
innerHTML?: string;
}
type AlterAssetTagGroupsHook = AsyncSeriesWaterfallHook<{
headTags: Array<HtmlTagObject | HtmlTagObject>;
bodyTags: Array<HtmlTagObject | HtmlTagObject>;
outputName: string;
plugin: HtmlWebpackPlugin;
}>;
export default class PublicURIWebpackPlugin implements Plugin {
private configTemplate: string;
private prefixTemplate: string;
@@ -33,16 +65,17 @@ export default class PublicURIWebpackPlugin implements Plugin {
};
public apply = (compiler: Compiler) => {
compiler.hooks.compilation.tap("CDNWebpackPlugin", compilation => {
(compilation.hooks as Hooks).htmlWebpackPluginAlterAssetTags.tapAsync(
"CDNWebpackPlugin",
compiler.hooks.compilation.tap("PublicURIWebpackPlugin", compilation => {
const hooks = (HtmlWebpackPlugin as any).getHooks(compilation);
(hooks.alterAssetTagGroups as AlterAssetTagGroupsHook).tapAsync(
"PublicURIWebpackPlugin",
(htmlPluginData, cb) => {
// Prefix all the asset's url's with the template.
htmlPluginData.head.forEach(this.prefixTag);
htmlPluginData.body.forEach(this.prefixTag);
htmlPluginData.headTags.forEach(this.prefixTag);
htmlPluginData.bodyTags.forEach(this.prefixTag);
// Insert the public path reference.
htmlPluginData.body.unshift({
htmlPluginData.bodyTags.unshift({
tagName: "script",
attributes: {
type: "application/json",
+1
View File
@@ -1 +1,2 @@
require("@babel/polyfill");
require("intersection-observer");
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>Talk - Auth Callback</title>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no">
</head>
<body></body>
</html>
+38
View File
@@ -0,0 +1,38 @@
import { authRedirectBackTo as key } from "talk-framework/helpers/storageKeys";
try {
// Pull the redirection
const value = sessionStorage.getItem(key);
if (!value) {
throw new Error(`${key} session storage key not set`);
}
if (process.env.NODE_ENV === "production") {
// Remove the redirect URL that we pulled from sessionStorage.
sessionStorage.removeItem(key);
}
// Parse the URL from the redirect parameter, and pull out the pathname.
const parser = document.createElement("a");
parser.href = value;
const redirectBackTo = parser.pathname + parser.search;
if (!redirectBackTo) {
throw new Error(`url stored in the ${key} session storage key was invalid`);
}
if (process.env.NODE_ENV !== "production") {
// Remove the redirect URL that we pulled from sessionStorage.
sessionStorage.removeItem(key);
}
// Now that we have a valid redirection URL, we should append the current
// hash that includes the credentials or errors from the callback.
const redirectBackToWithToken = redirectBackTo + location.hash;
// Send the user off to the redirection URL.
location.href = redirectBackToWithToken;
} catch (err) {
// Place the error message right into the document body.
document.body.appendChild(document.createTextNode(err.message));
}
@@ -2,7 +2,7 @@
exports[`renders sign in 1`] = `
<div>
<withContext(createMutationContainer(withContext(createMutationContainer(Relay(SignInContainer)))))
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(Relay(SignInContainer)))))))))
auth={Object {}}
/>
</div>
@@ -1,4 +1,6 @@
import { authRedirectBackTo } from "talk-framework/helpers/storageKeys";
export default function redirectOAuth2(redirectURL: string) {
sessionStorage.setItem("authRedirectBackTo", window.location.pathname);
sessionStorage.setItem(authRedirectBackTo, window.location.pathname);
window.location.href = redirectURL;
}
+3
View File
@@ -42,6 +42,9 @@ async function main() {
);
ReactDOM.render(<Index />, document.getElementById("app"));
// Set width.
window.resizeTo(350, window.outerHeight);
// Poll height.
pollPopupHeight();
}
@@ -2,29 +2,16 @@
exports[`get auth token from url 1`] = `
"{
\\"client:root\\": {
\\"__id\\": \\"client:root\\",
\\"__typename\\": \\"__Root\\",
\\"local\\": {
\\"__ref\\": \\"client:root.local\\"
}
\\"__id\\": \\"client:root.local\\",
\\"__typename\\": \\"Local\\",
\\"authToken\\": \\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzMWIyNjU5MS00ZTlhLTQzODgtYTdmZi1lMWJkYzVkOTdjY2UifQ==\\",
\\"authJTI\\": \\"31b26591-4e9a-4388-a7ff-e1bdc5d97cce\\",
\\"loggedIn\\": true,
\\"network\\": {
\\"__ref\\": \\"client:root.local.network\\"
},
\\"client:root.local\\": {
\\"__id\\": \\"client:root.local\\",
\\"__typename\\": \\"Local\\",
\\"authToken\\": \\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzMWIyNjU5MS00ZTlhLTQzODgtYTdmZi1lMWJkYzVkOTdjY2UifQ==\\",
\\"authJTI\\": \\"31b26591-4e9a-4388-a7ff-e1bdc5d97cce\\",
\\"loggedIn\\": true,
\\"network\\": {
\\"__ref\\": \\"client:root.local.network\\"
},
\\"view\\": \\"SIGN_IN\\"
},
\\"client:root.local.network\\": {
\\"__id\\": \\"client:root.local.network\\",
\\"__typename\\": \\"Network\\",
\\"isOffline\\": false
}
\\"view\\": \\"SIGN_IN\\",
\\"error\\": null
}"
`;
@@ -47,7 +34,8 @@ exports[`init local state 1`] = `
\\"network\\": {
\\"__ref\\": \\"client:root.local.network\\"
},
\\"view\\": \\"SIGN_IN\\"
\\"view\\": \\"SIGN_IN\\",
\\"error\\": null
},
\\"client:root.local.network\\": {
\\"__id\\": \\"client:root.local.network\\",
@@ -41,9 +41,18 @@ it("set view from query", async () => {
it("get auth token from url", async () => {
const restoreHistoryLocation = replaceHistoryLocation(
`http://localhost/#${createAuthToken()}`
`http://localhost/#accessToken=${createAuthToken()}`
);
await initLocalState(environment, context as any);
expect(JSON.stringify(source.toJSON(), null, 2)).toMatchSnapshot();
expect(JSON.stringify(source.get(LOCAL_ID), null, 2)).toMatchSnapshot();
restoreHistoryLocation();
});
it("get error from url", async () => {
const restoreHistoryLocation = replaceHistoryLocation(
`http://localhost/#error=error`
);
await initLocalState(environment, context as any);
expect(source.get(LOCAL_ID)!.error).toBe("error");
restoreHistoryLocation();
});
+20 -11
View File
@@ -4,17 +4,22 @@ import { parseQuery } from "talk-common/utils";
import { TalkContext } from "talk-framework/lib/bootstrap";
import { initLocalBaseState, LOCAL_ID } from "talk-framework/lib/relay";
function getAuthTokenFromHashAndClearIt() {
const authToken = window.location.hash
? window.location.hash.substr(1)
: null;
function getParamsFromHashAndClearIt() {
try {
const params = window.location.hash
? parseQuery(window.location.hash.substr(1))
: {};
// Remove hash with token.
if (window.location.hash) {
window.history.replaceState(null, document.title, location.pathname);
// Remove hash with token.
if (window.location.hash) {
window.history.replaceState(null, document.title, location.pathname);
}
return params;
} catch (err) {
window.console.error(err);
return {};
}
return authToken;
}
/**
@@ -24,8 +29,9 @@ export default async function initLocalState(
environment: Environment,
context: TalkContext
) {
const authToken = getAuthTokenFromHashAndClearIt();
await initLocalBaseState(environment, context, authToken);
const { error = null, accessToken = null } = getParamsFromHashAndClearIt();
await initLocalBaseState(environment, context, accessToken);
commitLocalUpdate(environment, s => {
const localRecord = s.get(LOCAL_ID)!;
@@ -35,5 +41,8 @@ export default async function initLocalState(
// Set default view.
localRecord.setValue(query.view || "SIGN_IN", "view");
// Set error.
localRecord.setValue(error, "error");
});
}
+1
View File
@@ -14,6 +14,7 @@ type Local {
authJTI: String
loggedIn: Boolean!
view: View!
error: String
}
extend type Query {
@@ -0,0 +1,23 @@
import { commitLocalUpdate, Environment } from "relay-runtime";
import { TalkContext } from "talk-framework/lib/bootstrap";
import { createMutationContainer } from "talk-framework/lib/relay";
import { LOCAL_ID } from "talk-framework/lib/relay/withLocalStateContainer";
export type ClearErrorMutation = () => Promise<void>;
export async function commit(
environment: Environment,
input: undefined,
{ pym }: TalkContext
) {
return commitLocalUpdate(environment, store => {
const record = store.get(LOCAL_ID)!;
record.setValue(null, "error");
});
}
export const withClearErrorMutation = createMutationContainer(
"clearError",
commit
);
@@ -15,7 +15,7 @@ export async function commit(
const result = await signIn(rest, pick(input, ["email", "password"]));
// Put the token on the hash and clean the session.
// It'll be picked up by initLocalState.
location.hash = result.token;
location.hash = `accessToken=${result.token}`;
clearSession();
}
@@ -18,7 +18,7 @@ export async function commit(
);
// Put the token on the hash and clean the session.
// It'll be picked up by initLocalState.
location.hash = result.token;
location.hash = `accessToken=${result.token}`;
clearSession();
}
+4
View File
@@ -14,3 +14,7 @@ export {
withSetPasswordMutation,
SetPasswordMutation,
} from "./SetPasswordMutation";
export {
withClearErrorMutation,
ClearErrorMutation,
} from "./ClearErrorMutation";
@@ -831,6 +831,181 @@ exports[`renders sign in view 1`] = `
</div>
`;
exports[`renders sign in view with error 1`] = `
<div
data-testid="signIn-container"
>
<div
className="Flex-root Bar-root Flex-flex Flex-justifyCenter Flex-alignCenter"
>
<div>
<h1
className="Typography-root Typography-heading2 Typography-colorTextPrimary Typography-alignCenter Title-root"
>
Sign In
</h1>
<h1
className="Typography-root Typography-heading4 Typography-colorTextPrimary Typography-alignCenter Subtitle-root"
>
to join the conversation
</h1>
</div>
</div>
<div
className="Flex-root SubBar-root Flex-flex Flex-justifyCenter Flex-alignCenter"
>
<div>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? 
</span>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
data-testid="gotoSignUpButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign Up
</button>
</div>
</div>
</div>
<div
className="Main-root"
data-testid="signIn-main"
>
<div
className="HorizontalGutter-root HorizontalGutter-oneAndAHalf"
>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
Social Login Error
</div>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
htmlFor="email"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
id="email"
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value=""
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
htmlFor="password"
>
Password
</label>
<div
className="PasswordField-fullWidth PasswordField-root"
>
<div
className="PasswordField-wrapper"
>
<input
className="PasswordField-colorRegular PasswordField-fullWidth PasswordField-input"
disabled={false}
id="password"
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="PasswordField-icon"
onClick={[Function]}
role="button"
tabIndex={0}
title="Show password"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
visibility
</span>
</div>
</div>
</div>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
data-testid="gotoForgotPasswordButton"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorBrand Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
<span
aria-hidden="true"
className="Icon-root ButtonIcon-root Icon-md"
>
email
</span>
<span>
Sign in with Email
</span>
</button>
</div>
</form>
<div
className="HorizontalGutter-root HorizontalGutter-full"
/>
</div>
</div>
</div>
`;
exports[`shows error when submitting empty form 1`] = `
<form
autoComplete="off"
+30 -2
View File
@@ -14,7 +14,10 @@ import mockWindow from "./mockWindow";
let windowMock: ReturnType<typeof mockWindow>;
async function createTestRenderer(customResolver: any = {}) {
async function createTestRenderer(
customResolver: any = {},
error: string | null = null
) {
const resolvers = {
...customResolver,
Query: {
@@ -31,6 +34,7 @@ async function createTestRenderer(customResolver: any = {}) {
resolvers,
initLocalState: localRecord => {
localRecord.setValue("SIGN_IN", "view");
localRecord.setValue(error, "error");
},
});
const container = await waitForElement(() =>
@@ -62,6 +66,30 @@ it("renders sign in view", async () => {
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("renders sign in view with error", async () => {
const { testRenderer, container } = await createTestRenderer(
{},
"Social Login Error"
);
expect(within(container).toJSON()).toMatchSnapshot();
within(testRenderer.root)
.getByTestID("gotoSignUpButton")
.props.onClick();
within(testRenderer.root)
.getByTestID("gotoSignInButton")
.props.onClick();
const container2 = await waitForElement(() =>
within(testRenderer.root).getByTestID("signIn-container")
);
// Error shouldn't be there anymore.
await wait(() =>
expect(
within(container2).queryByText("Social Login Error", { exact: false })
).toBeNull()
);
});
it("shows error when submitting empty form", async () => {
const { form } = await createTestRenderer();
form!.props.onSubmit();
@@ -170,7 +198,7 @@ it("submits form successfully", async () => {
expect(toJSON(form!)).toMatchSnapshot();
// Wait for window hash to contain a token.
await wait(() => expect(location.hash).toBe(`#${authToken}`));
await wait(() => expect(location.hash).toBe(`#accessToken=${authToken}`));
restMock.verify();
});
+1 -1
View File
@@ -224,7 +224,7 @@ it("submits form successfully", async () => {
expect(toJSON(main)).toMatchSnapshot();
// Wait for window hash to contain a token.
await wait(() => expect(location.hash).toBe(`#${authToken}`));
await wait(() => expect(location.hash).toBe(`#accessToken=${authToken}`));
restMock.verify();
});
@@ -17,6 +17,22 @@ it("renders correctly", () => {
googleEnabled: true,
oidcEnabled: true,
auth: {},
error: null,
};
const renderer = createRenderer();
renderer.render(<SignInN {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
it("renders error", () => {
const props: PropTypesOf<typeof SignInN> = {
onGotoSignUp: noop,
emailEnabled: true,
facebookEnabled: true,
googleEnabled: true,
oidcEnabled: true,
auth: {},
error: "Server Error",
};
const renderer = createRenderer();
renderer.render(<SignInN {...props} />);
@@ -31,6 +47,7 @@ it("renders without email login", () => {
googleEnabled: true,
oidcEnabled: true,
auth: {},
error: null,
};
const renderer = createRenderer();
renderer.render(<SignInN {...props} />);
@@ -6,7 +6,13 @@ import Main from "talk-auth/components/Main";
import OrSeparator from "talk-auth/components/OrSeparator";
import AutoHeightContainer from "talk-auth/containers/AutoHeightContainer";
import { PropTypesOf } from "talk-framework/types";
import { Button, Flex, HorizontalGutter, Typography } from "talk-ui/components";
import {
Button,
CallOut,
Flex,
HorizontalGutter,
Typography,
} from "talk-ui/components";
import SignInWithEmailContainer from "../containers/SignInWithEmailContainer";
import SignInWithFacebookContainer from "../containers/SignInWithFacebookContainer";
@@ -14,6 +20,7 @@ import SignInWithGoogleContainer from "../containers/SignInWithGoogleContainer";
import SignInWithOIDCContainer from "../containers/SignInWithOIDCContainer";
export interface SignInForm {
error: string | null;
onGotoSignUp: () => void;
emailEnabled?: boolean;
facebookEnabled?: boolean;
@@ -31,6 +38,7 @@ const SignIn: StatelessComponent<SignInForm> = ({
googleEnabled,
oidcEnabled,
auth,
error,
}) => {
const oneClickIntegrationEnabled =
facebookEnabled || googleEnabled || oidcEnabled;
@@ -68,6 +76,11 @@ const SignIn: StatelessComponent<SignInForm> = ({
</SubBar>
<Main data-testid="signIn-main">
<HorizontalGutter size="oneAndAHalf">
{error && (
<CallOut color="error" fullWidth>
{error}
</CallOut>
)}
{emailEnabled && <SignInWithEmailContainer />}
{emailEnabled && oneClickIntegrationEnabled && <OrSeparator />}
<HorizontalGutter>
@@ -59,6 +59,71 @@ exports[`renders correctly 1`] = `
</div>
`;
exports[`renders error 1`] = `
<div
data-testid="signIn-container"
>
<AutoHeightContainer />
<Localized
id="signIn-signInToJoinHeader"
subtitle={<Subtitle />}
title={<Title />}
>
<Bar>
&lt;title&gt;Sign In&lt;/title&gt;&lt;subtitle&gt;to join the conversation&lt;/subtitle&gt;
</Bar>
</Localized>
<SubBar>
<Localized
button={
<withPropsOnChange(Button)
color="primary"
data-testid="gotoSignUpButton"
onClick={[Function]}
size="small"
variant="underlined"
/>
}
id="signIn-noAccountSignUp"
>
<withPropsOnChange(Typography)
container={[Function]}
variant="bodyCopy"
>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</withPropsOnChange(Typography)>
</Localized>
</SubBar>
<Main
data-testid="signIn-main"
>
<withPropsOnChange(HorizontalGutter)
size="oneAndAHalf"
>
<withPropsOnChange(CallOut)
color="error"
fullWidth={true}
>
Server Error
</withPropsOnChange(CallOut)>
<withContext(createMutationContainer(withContext(createMutationContainer(SignInContainer)))) />
<OrSeparator />
<withPropsOnChange(HorizontalGutter)>
<Relay(SignInWithFacebookContainer)
auth={Object {}}
/>
<Relay(SignInWithGoogleContainer)
auth={Object {}}
/>
<Relay(SignInWithOIDCContainer)
auth={Object {}}
/>
</withPropsOnChange(HorizontalGutter)>
</withPropsOnChange(HorizontalGutter)>
</Main>
</div>
`;
exports[`renders without email login 1`] = `
<div
data-testid="signIn-container"
@@ -1,28 +1,43 @@
import React, { Component } from "react";
import { SignInContainer_auth as AuthData } from "talk-auth/__generated__/SignInContainer_auth.graphql";
import { SignInContainerLocal as LocalData } from "talk-auth/__generated__/SignInContainerLocal.graphql";
import {
ClearErrorMutation,
SetViewMutation,
SignInMutation,
withClearErrorMutation,
withSetViewMutation,
withSignInMutation,
} from "talk-auth/mutations";
import { graphql, withFragmentContainer } from "talk-framework/lib/relay";
import {
graphql,
withFragmentContainer,
withLocalStateContainer,
} from "talk-framework/lib/relay";
import SignIn from "../components/SignIn";
interface Props {
local: LocalData;
auth: AuthData;
signIn: SignInMutation;
setView: SetViewMutation;
clearError: ClearErrorMutation;
}
class SignInContainer extends Component<Props> {
private goToSignUp = () => this.props.setView({ view: "SIGN_UP" });
public componentWillUnmount() {
this.props.clearError();
}
public render() {
const integrations = this.props.auth.integrations;
return (
<SignIn
error={this.props.local.error}
auth={this.props.auth}
onGotoSignUp={this.goToSignUp}
emailEnabled={
@@ -44,42 +59,52 @@ class SignInContainer extends Component<Props> {
}
const enhanced = withSetViewMutation(
withSignInMutation(
withFragmentContainer<Props>({
auth: graphql`
fragment SignInContainer_auth on Auth {
...SignInWithOIDCContainer_auth
...SignInWithGoogleContainer_auth
...SignInWithFacebookContainer_auth
integrations {
local {
enabled
targetFilter {
stream
}
}
facebook {
enabled
targetFilter {
stream
}
}
google {
enabled
targetFilter {
stream
}
}
oidc {
enabled
targetFilter {
stream
}
}
withClearErrorMutation(
withSignInMutation(
withLocalStateContainer(
graphql`
fragment SignInContainerLocal on Local {
error
}
}
`,
})(SignInContainer)
`
)(
withFragmentContainer<Props>({
auth: graphql`
fragment SignInContainer_auth on Auth {
...SignInWithOIDCContainer_auth
...SignInWithGoogleContainer_auth
...SignInWithFacebookContainer_auth
integrations {
local {
enabled
targetFilter {
stream
}
}
facebook {
enabled
targetFilter {
stream
}
}
google {
enabled
targetFilter {
stream
}
}
oidc {
enabled
targetFilter {
stream
}
}
}
}
`,
})(SignInContainer)
)
)
)
);
export default enhanced;
@@ -0,0 +1 @@
export const authRedirectBackTo = "authRedirectBackTo";
@@ -4,6 +4,7 @@ export default (process.env.NODE_ENV !== "development"
embed: {
stream: "/embed/stream",
auth: "/embed/auth",
authCallback: "/embed/auth/callback",
},
}
: {
@@ -11,5 +12,6 @@ export default (process.env.NODE_ENV !== "development"
embed: {
stream: "/stream.html",
auth: "/auth.html",
authCallback: "/auth-callback.html",
},
});
@@ -1,7 +1,6 @@
import * as React from "react";
import { createContextHOC } from "talk-framework/helpers";
import ensurePolyfill from "./ensurePolyfill";
export type IntersectionCallback = (entry: IntersectionObserverEntry) => void;
export type Observe = (
@@ -24,18 +23,16 @@ export class IntersectionProvider extends React.Component<any, any> {
private unmounted = false;
public componentDidMount() {
ensurePolyfill().then(() => {
if (this.unmounted) {
return;
}
this.observer = new IntersectionObserver(this.onIntersect, {
root: this.props.node ? this.props.node : undefined,
rootMargin: "0px",
threshold: 0.25,
});
this.elementBuffer.forEach(element => this.observer.observe(element));
this.elementBuffer = [];
if (this.unmounted) {
return;
}
this.observer = new IntersectionObserver(this.onIntersect, {
root: this.props.node ? this.props.node : undefined,
rootMargin: "0px",
threshold: 0.25,
});
this.elementBuffer.forEach(element => this.observer.observe(element));
this.elementBuffer = [];
}
public componentWillUnmount() {
@@ -1,9 +0,0 @@
/**
* Loads intersection-observer polyfill if it doesn't exist.
*/
export default async function ensurePolyfill() {
if (!(window as any).IntersectionObserver) {
await import("intersection-observer");
}
return;
}
@@ -4,4 +4,3 @@ export {
withIntersectionContext,
} from "./IntersectionContext";
export { default as withInView } from "./withInView";
export { default as ensurePolyfill } from "./ensurePolyfill";
+1 -1
View File
@@ -20,7 +20,7 @@ function withStyles<T>(
resolvedClasses[k] += ` ${props.classes[k]}`;
} else if (process.env.NODE_ENV !== "production") {
// tslint:disable:next-line: no-console
console.warn("Extending non existant className", k);
console.warn("Extending non existent className", k);
}
});
}
@@ -4,7 +4,11 @@ import ms from "ms";
export const noCacheMiddleware: RequestHandler = (req, res, next) => {
// Set cache control headers to prevent browsers/cdn's from caching these
// requests.
res.set({ "Cache-Control": "no-cache, no-store, must-revalidate" });
res.set({
"Cache-Control": "private, no-cache, no-store, must-revalidate",
Expires: "-1",
Pragma: "no-cache",
});
next();
};
@@ -138,12 +138,23 @@ export async function handleSuccessfulLogin(
}
export async function handleOAuth2Callback(
user: User,
err: Error | null,
user: User | null,
signingConfig: JWTSigningConfig,
req: Request,
res: Response,
next: NextFunction
) {
const path = "/embed/auth/callback";
if (!user) {
if (!err) {
// TODO: (wyattjoh) replace with better error
err = new Error("user not on request");
}
return res.redirect(path + `#error=${encodeURIComponent(err.message)}`);
}
try {
// Talk is guaranteed at this point.
const { tenant } = req.talk!;
@@ -158,35 +169,10 @@ export async function handleOAuth2Callback(
// Grab the token.
const token = await signTokenString(signingConfig, user, options);
// Set the cache control headers.
res.header("Cache-Control", "private, no-cache, no-store, must-revalidate");
res.header("Expires", "-1");
res.header("Pragma", "no-cache");
// Send back the details!
res.send(
`<html>
<head></head>
<body>
<script type="text/javascript">
const redirect = sessionStorage.getItem("authRedirectBackTo");
if (!redirect) {
var textnode = document.createTextNode("'authRedirectBackTo' not set in Session Storage");
document.body.appendChild(textnode);
}
else if (redirect[0] !== '/') {
var textnode = document.createTextNode("'authRedirectBackTo' must begin with '/'");
document.body.appendChild(textnode);
}
else {
location.href = \`\${redirect}#${token}\`;
}
</script>
</body>
</html>`
);
res.redirect(path + `#accessToken=${token}`);
} catch (err) {
return next(err);
res.redirect(path + `#error=${encodeURIComponent(err.message)}`);
}
}
@@ -209,15 +195,7 @@ export const wrapOAuth2Authn = (
name,
{ ...options, session: false },
(err: Error | null, user: User | null) => {
if (err) {
return next(err);
}
if (!user) {
// TODO: (wyattjoh) replace with better error.
return next(new Error("no user on request"));
}
handleOAuth2Callback(user, signingConfig, req, res, next);
handleOAuth2Callback(err, user, signingConfig, req, res, next);
}
)(req, res, next);
+3 -2
View File
@@ -5,6 +5,7 @@ import {
logoutHandler,
signupHandler,
} from "talk-server/app/handlers/api/tenant/auth/local";
import { noCacheMiddleware } from "talk-server/app/middleware/cacheHeaders";
import {
wrapAuthn,
wrapOAuth2Authn,
@@ -24,8 +25,8 @@ function wrapPath(
strategy
);
router.get(path, handler);
router.get(path + "/callback", handler);
router.get(path, noCacheMiddleware, handler);
router.get(path + "/callback", noCacheMiddleware, handler);
}
export function createNewAuthRouter(app: AppOptions, options: RouterOptions) {
+8
View File
@@ -44,6 +44,14 @@ export async function createRouter(app: AppOptions, options: RouterOptions) {
cacheDuration: false,
})
);
router.use(
"/embed/auth/callback",
createClientTargetRouter({
staticURI,
view: "auth-callback",
cacheDuration: false,
})
);
// Add the standalone targets.
router.use(
+1 -1
View File
@@ -29,7 +29,7 @@ describe("extractJWTFromRequest", () => {
};
expect(extractJWTFromRequest((req as any) as Request)).toEqual(null);
req.url = "https://talk.coralproject.net/api?access_token=token";
req.url = "https://talk.coralproject.net/api?accessToken=token";
expect(extractJWTFromRequest((req as any) as Request)).toEqual("token");
});
+1 -1
View File
@@ -115,7 +115,7 @@ export const signPATString = async (
export function extractJWTFromRequest(req: Request) {
const permit = new Bearer({
basic: "password",
query: "access_token",
query: "accessToken",
});
return permit.check(req) || null;
+1 -1
View File
@@ -26,7 +26,7 @@ describe("extractJWTFromRequest", () => {
};
expect(extractJWTFromRequest((req as any) as Request)).toEqual(null);
req.url = "https://talk.coralproject.net/api?access_token=token";
req.url = "https://talk.coralproject.net/api?accessToken=token";
expect(extractJWTFromRequest((req as any) as Request)).toEqual("token");
});