Add captcha
This commit is contained in:
parent
8c87c01810
commit
ec1252554c
14 changed files with 287 additions and 16 deletions
183
backend/package-lock.json
generated
183
backend/package-lock.json
generated
|
@ -212,11 +212,24 @@
|
|||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz",
|
||||
"integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ=="
|
||||
},
|
||||
"@sqltools/formatter": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz",
|
||||
"integrity": "sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q=="
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
|
||||
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
|
||||
"requires": {
|
||||
"defer-to-connect": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@types/accepts": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz",
|
||||
|
@ -234,6 +247,17 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/cacheable-request": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
|
||||
"integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
|
||||
"requires": {
|
||||
"@types/http-cache-semantics": "*",
|
||||
"@types/keyv": "*",
|
||||
"@types/node": "*",
|
||||
"@types/responselike": "*"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz",
|
||||
|
@ -312,6 +336,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz",
|
||||
"integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ=="
|
||||
},
|
||||
"@types/http-cache-semantics": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
|
||||
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
|
||||
},
|
||||
"@types/http-errors": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz",
|
||||
|
@ -334,6 +363,14 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
|
||||
"integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw=="
|
||||
},
|
||||
"@types/keyv": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
|
||||
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/koa": {
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.6.tgz",
|
||||
|
@ -401,6 +438,14 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||
},
|
||||
"@types/responselike": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
|
||||
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/semver": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz",
|
||||
|
@ -1126,6 +1171,25 @@
|
|||
"dicer": "0.3.0"
|
||||
}
|
||||
},
|
||||
"cacheable-lookup": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="
|
||||
},
|
||||
"cacheable-request": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
|
||||
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
|
||||
"requires": {
|
||||
"clone-response": "^1.0.2",
|
||||
"get-stream": "^5.1.0",
|
||||
"http-cache-semantics": "^4.0.0",
|
||||
"keyv": "^4.0.0",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"normalize-url": "^4.1.0",
|
||||
"responselike": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
|
@ -1272,6 +1336,14 @@
|
|||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"clone-response": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
|
||||
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
|
||||
"requires": {
|
||||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
|
@ -1417,6 +1489,21 @@
|
|||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
|
||||
"dev": true
|
||||
},
|
||||
"decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"requires": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
|
||||
|
@ -1428,6 +1515,11 @@
|
|||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
|
||||
},
|
||||
"defer-to-connect": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
|
||||
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
|
@ -2292,6 +2384,14 @@
|
|||
"integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
|
||||
"dev": true
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
|
@ -2337,6 +2437,24 @@
|
|||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "11.8.1",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.1.tgz",
|
||||
"integrity": "sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q==",
|
||||
"requires": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.1",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"responselike": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
|
@ -2464,6 +2582,11 @@
|
|||
"entities": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
|
||||
|
@ -2483,6 +2606,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"http2-wrapper": {
|
||||
"version": "1.0.0-beta.5.2",
|
||||
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz",
|
||||
"integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==",
|
||||
"requires": {
|
||||
"quick-lru": "^5.1.1",
|
||||
"resolve-alpn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
|
@ -2683,6 +2815,11 @@
|
|||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
|
@ -2703,6 +2840,14 @@
|
|||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"keyv": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
|
||||
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
|
||||
"requires": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"leven": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
|
||||
|
@ -2787,6 +2932,11 @@
|
|||
"signal-exit": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lowercase-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
|
@ -2939,6 +3089,11 @@
|
|||
"mime-db": "1.45.0"
|
||||
}
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
|
@ -3039,6 +3194,11 @@
|
|||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-url": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
|
||||
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
|
||||
|
@ -3134,6 +3294,11 @@
|
|||
"word-wrap": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"p-cancelable": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
|
||||
"integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
|
||||
|
@ -3369,6 +3534,11 @@
|
|||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz",
|
||||
"integrity": "sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A=="
|
||||
},
|
||||
"quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
|
||||
},
|
||||
"random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
|
@ -3497,12 +3667,25 @@
|
|||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"resolve-alpn": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz",
|
||||
"integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA=="
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
},
|
||||
"responselike": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
|
||||
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
|
||||
"requires": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"ret": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz",
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"fastify-http-proxy": "^4.2.0",
|
||||
"fastify-sensible": "^3.1.0",
|
||||
"fastify-session": "^5.2.1",
|
||||
"got": "^11.8.1",
|
||||
"lodash": "^4.17.20",
|
||||
"mongodb": "^3.6.3",
|
||||
"nanoid": "^3.1.20",
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import { ApolloError } from 'apollo-server-fastify';
|
||||
|
||||
export class UnknownPromptError extends Error {
|
||||
export class CaptchaError extends ApolloError {
|
||||
public name = 'CaptchaError';
|
||||
|
||||
public constructor() {
|
||||
super('Captcha validation failed', 'CAPTCHA_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
export class UnknownPromptError extends ApolloError {
|
||||
public name = 'UnknownPromptError';
|
||||
|
||||
public constructor() {
|
||||
super('Unknown prompt');
|
||||
super('Unknown prompt', 'UNKNOWN_PROMPT_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ import {
|
|||
Arg, Ctx, Mutation, Resolver,
|
||||
} from 'type-graphql';
|
||||
import {
|
||||
encryptSymmetrical, encryptWithPublicKey, generatePrivatePublicPair, isObject,
|
||||
encryptSymmetrical, encryptWithPublicKey, generatePrivatePublicPair, isObject, verifyCaptchaResponse,
|
||||
} from '../../../utils';
|
||||
import { InvalidVulcanCredentialsError, UnknownPromptError } from '../errors';
|
||||
import { CaptchaError, InvalidVulcanCredentialsError, UnknownPromptError } from '../errors';
|
||||
import LoginResult from '../models/login-result';
|
||||
import type LoginResultStudent from '../models/login-result-student';
|
||||
import type { WebsiteAPIContext } from '../types';
|
||||
|
@ -21,10 +21,12 @@ export default class LoginResolver {
|
|||
@Arg('username') username: string,
|
||||
@Arg('password') password: string,
|
||||
@Arg('host') host: string,
|
||||
@Arg('captchaResponse') captchaResponse: string,
|
||||
@Ctx() { sessionData, reply }: WebsiteAPIContext,
|
||||
): Promise<LoginResult> {
|
||||
const prompt = sessionData.prompts.get(promptId);
|
||||
if (!prompt) throw new UnknownPromptError();
|
||||
if (!await verifyCaptchaResponse(captchaResponse)) throw new CaptchaError();
|
||||
const client = new Client(host, () => ({
|
||||
username,
|
||||
password,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import got from 'got';
|
||||
import _ from 'lodash';
|
||||
import { ParamError } from '../errors';
|
||||
import SessionData from '../session-data';
|
||||
|
@ -58,3 +59,20 @@ export function getSessionData(session: Session): SessionData {
|
|||
}
|
||||
return session.data;
|
||||
}
|
||||
|
||||
export async function verifyCaptchaResponse(response: string): Promise<boolean> {
|
||||
const { body } = await got.post<{
|
||||
success: boolean,
|
||||
challenge_ts?: string;
|
||||
hostname?: string;
|
||||
'error-codes'?: string[]
|
||||
}>('https://www.google.com/recaptcha/api/siteverify', {
|
||||
responseType: 'json',
|
||||
searchParams: {
|
||||
secret: requireEnv('CAPTCHA_SECRET'),
|
||||
response,
|
||||
},
|
||||
});
|
||||
console.log(body);
|
||||
return body.success;
|
||||
}
|
||||
|
|
1
website/.env.development
Normal file
1
website/.env.development
Normal file
|
@ -0,0 +1 @@
|
|||
VUE_APP_CAPTCHA_SITE_KEY=6LfAxzUaAAAAANF5VLy39hbgx5K6WTQTa2YDdhmC
|
|
@ -14,6 +14,7 @@ module.exports = {
|
|||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'import/prefer-default-export': ['warn']
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
|
|
5
website/package-lock.json
generated
5
website/package-lock.json
generated
|
@ -16083,6 +16083,11 @@
|
|||
"resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz",
|
||||
"integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ=="
|
||||
},
|
||||
"vue-recaptcha": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-recaptcha/-/vue-recaptcha-1.3.0.tgz",
|
||||
"integrity": "sha512-9Qf1niyHq4QbEUhsvdUkS8BoOyhYwpp8v+imUSj67ffDo9RQ6h8Ekq8EGnw/GKViXCwWalp7EEY/n/fOtU0FyA=="
|
||||
},
|
||||
"vue-style-loader": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"vue": "^2.6.11",
|
||||
"vue-class-component": "^7.2.3",
|
||||
"vue-property-decorator": "^9.1.2",
|
||||
"vue-recaptcha": "^1.3.0",
|
||||
"vuetify": "^2.2.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit" async defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
|
|
@ -17,19 +17,31 @@
|
|||
outlined
|
||||
:rules="requiredRules"
|
||||
/>
|
||||
<div class="overflow-x-auto">
|
||||
<vue-recaptcha
|
||||
ref="recaptcha"
|
||||
class="d-inline-block"
|
||||
:sitekey="captchaSiteKey"
|
||||
@verify="captchaVerify"
|
||||
@expired="captchaReset()"
|
||||
@error="captchaReset()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<v-alert type="error" class="mx-2" :value="error === 'invalid-credentials'">
|
||||
Dane logowania są nieprawidłowe
|
||||
</v-alert>
|
||||
<v-alert type="error" class="mx-2" :value="error === 'other'">
|
||||
Podczas logowania wystąpił błąd
|
||||
<v-alert type="error" class="mx-2" :value="errorMessage !== null">
|
||||
{{ errorMessage }}
|
||||
</v-alert>
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" text outlined @click="back" :disabled="loading">
|
||||
Wróć
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn color="primary" :loading="loading" type="submit" :disabled="!formValid">
|
||||
<v-btn
|
||||
color="primary"
|
||||
:loading="loading"
|
||||
type="submit"
|
||||
:disabled="!formValid || !this.captchaResponse"
|
||||
>
|
||||
Zaloguj się
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
@ -44,6 +56,8 @@ import {
|
|||
import { PromptInfo } from '@/types';
|
||||
import { hasErrorCode, sdk } from '@/pages/authenticate-prompt/sdk';
|
||||
import { InputValidationRules } from 'vuetify';
|
||||
import VueRecaptcha from 'vue-recaptcha';
|
||||
import { requireEnv } from '@/utils';
|
||||
|
||||
interface VForm extends HTMLFormElement {
|
||||
validate(): boolean;
|
||||
|
@ -52,6 +66,9 @@ interface VForm extends HTMLFormElement {
|
|||
|
||||
@Component({
|
||||
name: 'LoginWindow',
|
||||
components: {
|
||||
VueRecaptcha,
|
||||
},
|
||||
})
|
||||
export default class LoginWindow extends Vue {
|
||||
@Prop({
|
||||
|
@ -62,6 +79,10 @@ export default class LoginWindow extends Vue {
|
|||
|
||||
@Ref('form') form!: VForm;
|
||||
|
||||
@Ref('recaptcha') recaptcha!: VueRecaptcha;
|
||||
|
||||
readonly captchaSiteKey = requireEnv('VUE_APP_CAPTCHA_SITE_KEY');
|
||||
|
||||
readonly hosts = [
|
||||
{
|
||||
text: 'Vulcan',
|
||||
|
@ -85,19 +106,30 @@ export default class LoginWindow extends Vue {
|
|||
|
||||
password = '';
|
||||
|
||||
captchaResponse: string | null = null;
|
||||
|
||||
loading = false;
|
||||
|
||||
error: 'invalid-credentials' | 'other' | null = null;
|
||||
error: 'invalid-credentials' | 'other' | 'captcha' | null = null;
|
||||
|
||||
reset() {
|
||||
this.host = 'fakelog.cf';
|
||||
this.username = '';
|
||||
this.password = '';
|
||||
this.form.resetValidation();
|
||||
this.recaptcha.reset();
|
||||
}
|
||||
|
||||
captchaVerify(response: string) {
|
||||
this.captchaResponse = response;
|
||||
}
|
||||
|
||||
captchaReset() {
|
||||
this.captchaResponse = null;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.loading || !this.formValid) return;
|
||||
if (this.loading || !this.formValid || !this.captchaResponse) return;
|
||||
this.error = null;
|
||||
this.loading = true;
|
||||
try {
|
||||
|
@ -106,13 +138,16 @@ export default class LoginWindow extends Vue {
|
|||
host: this.host,
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
captchaResponse: this.captchaResponse,
|
||||
});
|
||||
const { students } = login;
|
||||
this.$emit('login', { students });
|
||||
this.reset();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.error = hasErrorCode(error, 'INVALID_VULCAN_CREDENTIALS') ? 'invalid-credentials' : 'other';
|
||||
if (hasErrorCode(error, 'INVALID_VULCAN_CREDENTIALS')) this.error = 'invalid-credentials';
|
||||
if (hasErrorCode(error, 'CAPTCHA_ERROR')) this.error = 'captcha';
|
||||
else this.error = 'other';
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
|
@ -125,5 +160,12 @@ export default class LoginWindow extends Vue {
|
|||
mounted() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
get errorMessage() {
|
||||
if (this.error === null) return null;
|
||||
if (this.error === 'invalid-credentials') return 'Dane logowania są nieprawidłowe';
|
||||
if (this.error === 'captcha') return 'Błąd weryfikacji';
|
||||
return 'Podczas logowania wystąpił błąd';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -53,6 +53,7 @@ export type Mutation = {
|
|||
};
|
||||
|
||||
export type MutationLoginArgs = {
|
||||
captchaResponse: Scalars['String'];
|
||||
host: Scalars['String'];
|
||||
password: Scalars['String'];
|
||||
username: Scalars['String'];
|
||||
|
@ -75,6 +76,7 @@ export type LoginMutationVariables = Exact<{
|
|||
host: Scalars['String'];
|
||||
username: Scalars['String'];
|
||||
password: Scalars['String'];
|
||||
captchaResponse: Scalars['String'];
|
||||
}>;
|
||||
|
||||
export type LoginMutation = (
|
||||
|
@ -105,12 +107,13 @@ export type GetPromptInfoQuery = (
|
|||
);
|
||||
|
||||
export const LoginDocument = gql`
|
||||
mutation Login($promptId: String!, $host: String!, $username: String!, $password: String!) {
|
||||
mutation Login($promptId: String!, $host: String!, $username: String!, $password: String!, $captchaResponse: String!) {
|
||||
login(
|
||||
host: $host
|
||||
password: $password
|
||||
username: $username
|
||||
promptId: $promptId
|
||||
captchaResponse: $captchaResponse
|
||||
) {
|
||||
students {
|
||||
studentId
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import gql from 'graphql-tag';
|
||||
|
||||
export default gql`mutation Login($promptId: String!, $host: String!, $username: String!, $password: String!) {
|
||||
login(host: $host, password: $password, username: $username, promptId: $promptId) {
|
||||
export default gql`mutation Login($promptId: String!, $host: String!, $username: String!, $password: String!, $captchaResponse: String!) {
|
||||
login(host: $host, password: $password, username: $username, promptId: $promptId, captchaResponse: $captchaResponse) {
|
||||
students {
|
||||
studentId
|
||||
name
|
||||
|
|
5
website/src/utils.ts
Normal file
5
website/src/utils.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export function requireEnv(name: string): string {
|
||||
const value = process.env[name];
|
||||
if (value === undefined) throw new Error(`Environment variable ${name} not set`);
|
||||
return value;
|
||||
}
|
Loading…
Reference in a new issue