Use Auth SDK (#1474)
* Use Auth SDK * Update auth provider hook * Update sdk module mapping * Update setting password * Fix no user details on first login * Update auth tests * Cleanups * Update SDK Update SDK Update SDK Update test recordings Update SDK * Implement SDK External Auth Update new password view Hnalde external logout Update SDK Fix logout external redirect * Fix login page style * Update SDK * Auth Provider cleanups Update and refactor auth Auth types cleanups and refactor * Update channel context provider * Fix login error handling * Logout immidiatelly non-staff user * Update test snapshots * Trigger CI * Update to SDK v0.4, remove duplicated UserContext hook * Handle server errors during login * Fix wrong login page form submition handling * Update login error messages Co-authored-by: Jakub Majorek <majorek.jakub@gmail.com>
This commit is contained in:
parent
866bb91177
commit
4880093f63
59 changed files with 1005 additions and 1610 deletions
|
@ -1238,20 +1238,26 @@
|
||||||
"context": "link",
|
"context": "link",
|
||||||
"string": "Use this link to recover it"
|
"string": "Use this link to recover it"
|
||||||
},
|
},
|
||||||
"src_dot_auth_dot_components_dot_LoginPage_dot_3476994590": {
|
|
||||||
"string": "Sorry, your username and/or password are incorrect. Please try again."
|
|
||||||
},
|
|
||||||
"src_dot_auth_dot_components_dot_LoginPage_dot_3762459576": {
|
"src_dot_auth_dot_components_dot_LoginPage_dot_3762459576": {
|
||||||
"context": "description",
|
"context": "description",
|
||||||
"string": "or login using"
|
"string": "or login using"
|
||||||
},
|
},
|
||||||
"src_dot_auth_dot_components_dot_LoginPage_dot_534894384": {
|
|
||||||
"string": "Sorry, login went wrong. Please try again."
|
|
||||||
},
|
|
||||||
"src_dot_auth_dot_components_dot_LoginPage_dot_599516345": {
|
"src_dot_auth_dot_components_dot_LoginPage_dot_599516345": {
|
||||||
"context": "description",
|
"context": "description",
|
||||||
"string": "Forgot password? {resetPasswordLink}"
|
"string": "Forgot password? {resetPasswordLink}"
|
||||||
},
|
},
|
||||||
|
"src_dot_auth_dot_components_dot_LoginPage_dot_externalLoginError": {
|
||||||
|
"context": "error message",
|
||||||
|
"string": "Sorry, login went wrong. Please try again."
|
||||||
|
},
|
||||||
|
"src_dot_auth_dot_components_dot_LoginPage_dot_loginError": {
|
||||||
|
"context": "error message",
|
||||||
|
"string": "Sorry, your username and/or password are incorrect. Please try again."
|
||||||
|
},
|
||||||
|
"src_dot_auth_dot_components_dot_LoginPage_dot_serverError": {
|
||||||
|
"context": "error message",
|
||||||
|
"string": "Saleor is unavailable, please check your network connection and try again."
|
||||||
|
},
|
||||||
"src_dot_auth_dot_components_dot_NewPasswordPage_dot_1254879564": {
|
"src_dot_auth_dot_components_dot_NewPasswordPage_dot_1254879564": {
|
||||||
"string": "New Password"
|
"string": "New Password"
|
||||||
},
|
},
|
||||||
|
|
120
package-lock.json
generated
120
package-lock.json
generated
|
@ -4,6 +4,92 @@
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@apollo/client": {
|
||||||
|
"version": "3.4.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.4.15.tgz",
|
||||||
|
"integrity": "sha512-CnlT9i7TgHagkKQNvti81A9KcbIMqgpUPGJJL6bg5spTsB2R/5J6E7qiPcMvXuuXwR2xe4FmE4Ey4HizStb8Hg==",
|
||||||
|
"requires": {
|
||||||
|
"@graphql-typed-document-node/core": "^3.0.0",
|
||||||
|
"@wry/context": "^0.6.0",
|
||||||
|
"@wry/equality": "^0.5.0",
|
||||||
|
"@wry/trie": "^0.3.0",
|
||||||
|
"graphql-tag": "^2.12.3",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"optimism": "^0.16.1",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"symbol-observable": "^4.0.0",
|
||||||
|
"ts-invariant": "^0.9.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"zen-observable-ts": "~1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/zen-observable": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw=="
|
||||||
|
},
|
||||||
|
"@wry/context": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@wry/equality": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"graphql-tag": {
|
||||||
|
"version": "2.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.5.tgz",
|
||||||
|
"integrity": "sha512-5xNhP4063d16Pz3HBtKprutsPrmHZi5IdUGOWRxA2B6VF7BIRGOHZ5WQvDmJXZuPcBg7rYwaFxvQYjqkSdR3TQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"optimism": {
|
||||||
|
"version": "0.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/optimism/-/optimism-0.16.1.tgz",
|
||||||
|
"integrity": "sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg==",
|
||||||
|
"requires": {
|
||||||
|
"@wry/context": "^0.6.0",
|
||||||
|
"@wry/trie": "^0.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"symbol-observable": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ=="
|
||||||
|
},
|
||||||
|
"ts-invariant": {
|
||||||
|
"version": "0.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.9.3.tgz",
|
||||||
|
"integrity": "sha512-HinBlTbFslQI0OHP07JLsSXPibSegec6r9ai5xxq/qHYCsIQbzpymLpDhAUsnXcSrDEcd0L62L8vsOEdzM0qlA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
},
|
||||||
|
"zen-observable-ts": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/zen-observable": "0.8.3",
|
||||||
|
"zen-observable": "0.8.15"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@apollo/federation": {
|
"@apollo/federation": {
|
||||||
"version": "0.20.7",
|
"version": "0.20.7",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/federation/-/federation-0.20.7.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/federation/-/federation-0.20.7.tgz",
|
||||||
|
@ -3069,6 +3155,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@graphql-typed-document-node/core": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg=="
|
||||||
|
},
|
||||||
"@hapi/address": {
|
"@hapi/address": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
||||||
|
@ -5187,6 +5278,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@saleor/sdk": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@saleor/sdk/-/sdk-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-1wJ1WC73uAX7OBcZ4qRDJBEFVKrc2yXbqlbkFTjdRcRT+0k/T9RUPHZNIgFP0mj4/v6y6fyd9lgkknnF5vJnxw==",
|
||||||
|
"requires": {
|
||||||
|
"cross-fetch": "^3.1.4",
|
||||||
|
"jwt-decode": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@samverschueren/stream-to-observable": {
|
"@samverschueren/stream-to-observable": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz",
|
||||||
|
@ -7414,6 +7514,21 @@
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@wry/trie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
|
@ -20067,6 +20182,11 @@
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jwt-decode": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
|
||||||
|
},
|
||||||
"keycode": {
|
"keycode": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"npm": ">=6.11.0 <7"
|
"npm": ">=6.11.0 <7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@apollo/client": "^3.4.15",
|
||||||
"@editorjs/editorjs": "^2.19.3",
|
"@editorjs/editorjs": "^2.19.3",
|
||||||
"@editorjs/header": "^2.6.1",
|
"@editorjs/header": "^2.6.1",
|
||||||
"@editorjs/image": "^2.6.0",
|
"@editorjs/image": "^2.6.0",
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
"@material-ui/lab": "^4.0.0-alpha.58",
|
"@material-ui/lab": "^4.0.0-alpha.58",
|
||||||
"@material-ui/styles": "^4.11.4",
|
"@material-ui/styles": "^4.11.4",
|
||||||
"@saleor/macaw-ui": "^0.2.7",
|
"@saleor/macaw-ui": "^0.2.7",
|
||||||
|
"@saleor/sdk": "^0.4.0",
|
||||||
"@sentry/react": "^6.0.0",
|
"@sentry/react": "^6.0.0",
|
||||||
"@types/faker": "^5.1.6",
|
"@types/faker": "^5.1.6",
|
||||||
"@uiw/react-color-hue": "0.0.34",
|
"@uiw/react-color-hue": "0.0.34",
|
||||||
|
@ -118,7 +120,6 @@
|
||||||
"@types/fuzzaldrin": "^2.1.2",
|
"@types/fuzzaldrin": "^2.1.2",
|
||||||
"@types/jest": "^24.0.24",
|
"@types/jest": "^24.0.24",
|
||||||
"@types/lodash-es": "^4.17.3",
|
"@types/lodash-es": "^4.17.3",
|
||||||
"@types/node-fetch": "^2.5.7",
|
|
||||||
"@types/pollyjs__adapter-node-http": "^2.0.1",
|
"@types/pollyjs__adapter-node-http": "^2.0.1",
|
||||||
"@types/pollyjs__persister-fs": "^2.0.1",
|
"@types/pollyjs__persister-fs": "^2.0.1",
|
||||||
"@types/react": "^16.9.16",
|
"@types/react": "^16.9.16",
|
||||||
|
@ -169,7 +170,6 @@
|
||||||
"jest-localstorage-mock": "^2.4.3",
|
"jest-localstorage-mock": "^2.4.3",
|
||||||
"lint-staged": "^10.5.1",
|
"lint-staged": "^10.5.1",
|
||||||
"mock-apollo-client": "^0.4.0",
|
"mock-apollo-client": "^0.4.0",
|
||||||
"node-fetch": "^2.6.1",
|
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
"react-intl-translations-manager": "^5.0.3",
|
"react-intl-translations-manager": "^5.0.3",
|
||||||
"react-test-renderer": "^16.12.0",
|
"react-test-renderer": "^16.12.0",
|
||||||
|
@ -220,7 +220,7 @@
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"@assets(.*)$": "<rootDir>/assets/$1",
|
"@assets(.*)$": "<rootDir>/assets/$1",
|
||||||
"@locale(.*)$": "<rootDir>/locale/$1",
|
"@locale(.*)$": "<rootDir>/locale/$1",
|
||||||
"@saleor(?!.*macaw)(.*)$": "<rootDir>/src/$1",
|
"@saleor(?!.*macaw)(?!.*sdk)(.*)$": "<rootDir>/src/$1",
|
||||||
"@test/(.*)$": "<rootDir>/testUtils/$1",
|
"@test/(.*)$": "<rootDir>/testUtils/$1",
|
||||||
"^lodash-es(.*)$": "lodash/$1",
|
"^lodash-es(.*)$": "lodash/$1",
|
||||||
"^@material-ui/core$": "<rootDir>/node_modules/@material-ui/core",
|
"^@material-ui/core$": "<rootDir>/node_modules/@material-ui/core",
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
{
|
|
||||||
"log": {
|
|
||||||
"_recordingName": "User/will be logged if has valid token",
|
|
||||||
"creator": {
|
|
||||||
"comment": "persister:fs",
|
|
||||||
"name": "Polly.JS",
|
|
||||||
"version": "4.3.0"
|
|
||||||
},
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"_id": "a3088678db2635ada66ab049f76c9722",
|
|
||||||
"_order": 0,
|
|
||||||
"cache": {},
|
|
||||||
"request": {
|
|
||||||
"bodySize": 702,
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept",
|
|
||||||
"value": "*/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "702"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "user-agent",
|
|
||||||
"value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept-encoding",
|
|
||||||
"value": "gzip,deflate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "connection",
|
|
||||||
"value": "close"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "host",
|
|
||||||
"value": "localhost:8000"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 254,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"method": "POST",
|
|
||||||
"postData": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"params": [],
|
|
||||||
"text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMDYzMjg0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU5BPT0iLCJpc19zdGFmZiI6dHJ1ZX0.QDp4vlm1tKhk8iFnY2MnREvO-IubI5j8g_Wylb1XJqc\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
|
||||||
},
|
|
||||||
"queryString": [],
|
|
||||||
"url": "http://localhost:8000/graphql/"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"bodySize": 1765,
|
|
||||||
"content": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"size": 1765,
|
|
||||||
"text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1610632544, \"exp\": 1610632844, \"token\": \"ksEVMvgg6Bfi\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyNA==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyNA==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\", \"name\": \"Manage product types and attributes.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]"
|
|
||||||
},
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"name": "date",
|
|
||||||
"value": "Thu, 14 Jan 2021 14:10:40 GMT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "server",
|
|
||||||
"value": "WSGIServer/0.2 CPython/3.8.7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "1765"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "x-content-type-options",
|
|
||||||
"value": "nosniff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "referrer-policy",
|
|
||||||
"value": "same-origin"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 194,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"redirectURL": "",
|
|
||||||
"status": 200,
|
|
||||||
"statusText": "OK"
|
|
||||||
},
|
|
||||||
"startedDateTime": "2021-01-14T14:10:40.434Z",
|
|
||||||
"time": 155,
|
|
||||||
"timings": {
|
|
||||||
"blocked": -1,
|
|
||||||
"connect": -1,
|
|
||||||
"dns": -1,
|
|
||||||
"receive": 0,
|
|
||||||
"send": 0,
|
|
||||||
"ssl": -1,
|
|
||||||
"wait": 155
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pages": [],
|
|
||||||
"version": "1.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,11 +8,11 @@
|
||||||
},
|
},
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"_id": "4062f0ffdf1fab89a50cd1b667a889ef",
|
"_id": "2c067f705383db903cdaf88cb881c922",
|
||||||
"_order": 0,
|
"_order": 0,
|
||||||
"cache": {},
|
"cache": {},
|
||||||
"request": {
|
"request": {
|
||||||
"bodySize": 583,
|
"bodySize": 1154,
|
||||||
"cookies": [],
|
"cookies": [],
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
"name": "content-length",
|
"name": "content-length",
|
||||||
"value": "583"
|
"value": "1154"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
|
@ -50,40 +50,43 @@
|
||||||
"value": "localhost:8000"
|
"value": "localhost:8000"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headersSize": 281,
|
"headersSize": 255,
|
||||||
"httpVersion": "HTTP/1.1",
|
"httpVersion": "HTTP/1.1",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"postData": {
|
"postData": {
|
||||||
"mimeType": "application/json",
|
"mimeType": "application/json",
|
||||||
"params": [],
|
"params": [],
|
||||||
"text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
"text": "{\"operationName\":\"login\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment AccountErrorFragment on AccountError {\\n code\\n field\\n message\\n __typename\\n}\\n\\nfragment AddressFragment on Address {\\n id\\n firstName\\n lastName\\n companyName\\n streetAddress1\\n streetAddress2\\n city\\n cityArea\\n postalCode\\n country {\\n code\\n country\\n __typename\\n }\\n countryArea\\n phone\\n isDefaultBillingAddress\\n isDefaultShippingAddress\\n __typename\\n}\\n\\nfragment UserFragment on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n metadata {\\n key\\n value\\n __typename\\n }\\n defaultShippingAddress {\\n ...AddressFragment\\n __typename\\n }\\n defaultBillingAddress {\\n ...AddressFragment\\n __typename\\n }\\n addresses {\\n ...AddressFragment\\n __typename\\n }\\n __typename\\n}\\n\\nmutation login($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n csrfToken\\n token\\n errors {\\n ...AccountErrorFragment\\n __typename\\n }\\n user {\\n ...UserFragment\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}"
|
||||||
},
|
},
|
||||||
"queryString": [],
|
"queryString": [],
|
||||||
"url": "http://localhost:8000/graphql/"
|
"url": "http://localhost:8000/graphql/"
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"bodySize": 1976,
|
"bodySize": 679,
|
||||||
"content": {
|
"content": {
|
||||||
"mimeType": "application/json",
|
"mimeType": "application/json",
|
||||||
"size": 1976,
|
"size": 679,
|
||||||
"text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"UIzzJSFalS8pplfM1j5QNIUNiXb0VFH3kbe6kfTddYvLjJ9DhMasCtHJKXoGDfbw\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMDYzMjg0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU5BPT0iLCJpc19zdGFmZiI6dHJ1ZX0.QDp4vlm1tKhk8iFnY2MnREvO-IubI5j8g_Wylb1XJqc\", \"user\": {\"id\": \"VXNlcjoyNA==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\", \"name\": \"Manage product types and attributes.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]"
|
"text": "{\"data\": {\"tokenCreate\": {\"csrfToken\": \"yDcn3nGgfW2AXuCc62pvaLGtL0dKs7n513jhL30DqmDirUDXL4zwEJYQfji8L2yP\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzM5NTEwNzMsIm93bmVyIjoic2FsZW9yIiwiZXhwIjoxNjMzOTUxMzczLCJ0b2tlbiI6ImdtRU95YlM5TDhmaiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJ0eXBlIjoiYWNjZXNzIiwidXNlcl9pZCI6IlZYTmxjam94IiwiaXNfc3RhZmYiOnRydWV9.hf105U1yvWzf-X_bKit4jh6vU_EnCC52XupS9GVlKaw\", \"errors\": [], \"user\": {\"id\": \"VXNlcjox\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"metadata\": [], \"defaultShippingAddress\": null, \"defaultBillingAddress\": null, \"addresses\": [], \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}"
|
||||||
},
|
},
|
||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
|
"expires": "2021-11-10T11:17:53.000Z",
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
|
"maxAge": 2592000,
|
||||||
"name": "refreshToken",
|
"name": "refreshToken",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ"
|
"sameSite": "Lax",
|
||||||
|
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzM5NTEwNzMsIm93bmVyIjoic2FsZW9yIiwiZXhwIjoxNjM2NTQzMDczLCJ0b2tlbiI6ImdtRU95YlM5TDhmaiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJ0eXBlIjoicmVmcmVzaCIsInVzZXJfaWQiOiJWWE5sY2pveCIsImlzX3N0YWZmIjp0cnVlLCJjc3JmVG9rZW4iOiJ5RGNuM25HZ2ZXMkFYdUNjNjJwdmFMR3RMMGRLczduNTEzamhMMzBEcW1EaXJVRFhMNHp3RUpZUWZqaThMMnlQIn0.zRkkzPiC8DPlBUvUPVcOSrEF4AzgDKcK8gpyQXSpKM4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
"name": "date",
|
"name": "date",
|
||||||
"value": "Thu, 14 Jan 2021 13:55:44 GMT"
|
"value": "Mon, 11 Oct 2021 11:17:53 GMT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"value": "WSGIServer/0.2 CPython/3.8.7"
|
"value": "WSGIServer/0.2 CPython/3.8.3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "content-type",
|
"name": "content-type",
|
||||||
|
@ -91,7 +94,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "content-length",
|
"name": "content-length",
|
||||||
"value": "1976"
|
"value": "679"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "x-content-type-options",
|
"name": "x-content-type-options",
|
||||||
|
@ -104,17 +107,17 @@
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
"name": "set-cookie",
|
"name": "set-cookie",
|
||||||
"value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ; HttpOnly; Path=/"
|
"value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzM5NTEwNzMsIm93bmVyIjoic2FsZW9yIiwiZXhwIjoxNjM2NTQzMDczLCJ0b2tlbiI6ImdtRU95YlM5TDhmaiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJ0eXBlIjoicmVmcmVzaCIsInVzZXJfaWQiOiJWWE5sY2pveCIsImlzX3N0YWZmIjp0cnVlLCJjc3JmVG9rZW4iOiJ5RGNuM25HZ2ZXMkFYdUNjNjJwdmFMR3RMMGRLczduNTEzamhMMzBEcW1EaXJVRFhMNHp3RUpZUWZqaThMMnlQIn0.zRkkzPiC8DPlBUvUPVcOSrEF4AzgDKcK8gpyQXSpKM4; expires=Wed, 10 Nov 2021 11:17:53 GMT; HttpOnly; Max-Age=2592000; Path=/; SameSite=Lax"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headersSize": 618,
|
"headersSize": 704,
|
||||||
"httpVersion": "HTTP/1.1",
|
"httpVersion": "HTTP/1.1",
|
||||||
"redirectURL": "",
|
"redirectURL": "",
|
||||||
"status": 200,
|
"status": 200,
|
||||||
"statusText": "OK"
|
"statusText": "OK"
|
||||||
},
|
},
|
||||||
"startedDateTime": "2021-04-22T11:07:20.652Z",
|
"startedDateTime": "2021-10-11T11:17:52.646Z",
|
||||||
"time": 758,
|
"time": 521,
|
||||||
"timings": {
|
"timings": {
|
||||||
"blocked": -1,
|
"blocked": -1,
|
||||||
"connect": -1,
|
"connect": -1,
|
||||||
|
@ -122,7 +125,7 @@
|
||||||
"receive": 0,
|
"receive": 0,
|
||||||
"send": 0,
|
"send": 0,
|
||||||
"ssl": -1,
|
"ssl": -1,
|
||||||
"wait": 758
|
"wait": 521
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
{
|
|
||||||
"log": {
|
|
||||||
"_recordingName": "User/will not be logged if has expired token",
|
|
||||||
"creator": {
|
|
||||||
"comment": "persister:fs",
|
|
||||||
"name": "Polly.JS",
|
|
||||||
"version": "4.3.0"
|
|
||||||
},
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"_id": "414f6b24b58b132c92e9a6ea67613a15",
|
|
||||||
"_order": 0,
|
|
||||||
"cache": {},
|
|
||||||
"request": {
|
|
||||||
"bodySize": 691,
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept",
|
|
||||||
"value": "*/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "691"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "user-agent",
|
|
||||||
"value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept-encoding",
|
|
||||||
"value": "gzip,deflate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "connection",
|
|
||||||
"value": "close"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "host",
|
|
||||||
"value": "localhost:8000"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 254,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"method": "POST",
|
|
||||||
"postData": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"params": [],
|
|
||||||
"text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo4\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
|
||||||
},
|
|
||||||
"queryString": [],
|
|
||||||
"url": "http://localhost:8000/graphql/"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"bodySize": 89,
|
|
||||||
"content": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"size": 89,
|
|
||||||
"text": "[{\"data\": {\"tokenVerify\": {\"payload\": null, \"user\": null, \"__typename\": \"VerifyToken\"}}}]"
|
|
||||||
},
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"name": "date",
|
|
||||||
"value": "Tue, 21 Jul 2020 13:43:50 GMT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "server",
|
|
||||||
"value": "WSGIServer/0.2 CPython/3.8.1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "access-control-allow-origin",
|
|
||||||
"value": "*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "access-control-allow-methods",
|
|
||||||
"value": "POST, OPTIONS"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "access-control-allow-headers",
|
|
||||||
"value": "Origin, Content-Type, Accept, Authorization"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "89"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "x-content-type-options",
|
|
||||||
"value": "nosniff"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 314,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"redirectURL": "",
|
|
||||||
"status": 200,
|
|
||||||
"statusText": "OK"
|
|
||||||
},
|
|
||||||
"startedDateTime": "2020-07-21T13:43:50.249Z",
|
|
||||||
"time": 17,
|
|
||||||
"timings": {
|
|
||||||
"blocked": -1,
|
|
||||||
"connect": -1,
|
|
||||||
"dns": -1,
|
|
||||||
"receive": 0,
|
|
||||||
"send": 0,
|
|
||||||
"ssl": -1,
|
|
||||||
"wait": 17
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pages": [],
|
|
||||||
"version": "1.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
{
|
|
||||||
"log": {
|
|
||||||
"_recordingName": "User/will not be logged if has invalid token",
|
|
||||||
"creator": {
|
|
||||||
"comment": "persister:fs",
|
|
||||||
"name": "Polly.JS",
|
|
||||||
"version": "4.3.0"
|
|
||||||
},
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"_id": "b1557b45bbbf7aed1a4a53f5141ca324",
|
|
||||||
"_order": 0,
|
|
||||||
"cache": {},
|
|
||||||
"request": {
|
|
||||||
"bodySize": 439,
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept",
|
|
||||||
"value": "*/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "439"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "user-agent",
|
|
||||||
"value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept-encoding",
|
|
||||||
"value": "gzip,deflate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "connection",
|
|
||||||
"value": "close"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "host",
|
|
||||||
"value": "localhost:8000"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 254,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"method": "POST",
|
|
||||||
"postData": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"params": [],
|
|
||||||
"text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"NotAToken\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
|
||||||
},
|
|
||||||
"queryString": [],
|
|
||||||
"url": "http://localhost:8000/graphql/"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"bodySize": 89,
|
|
||||||
"content": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"size": 89,
|
|
||||||
"text": "[{\"data\": {\"tokenVerify\": {\"payload\": null, \"user\": null, \"__typename\": \"VerifyToken\"}}}]"
|
|
||||||
},
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"name": "date",
|
|
||||||
"value": "Thu, 14 Jan 2021 14:10:40 GMT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "server",
|
|
||||||
"value": "WSGIServer/0.2 CPython/3.8.7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "89"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "x-content-type-options",
|
|
||||||
"value": "nosniff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "referrer-policy",
|
|
||||||
"value": "same-origin"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 192,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"redirectURL": "",
|
|
||||||
"status": 200,
|
|
||||||
"statusText": "OK"
|
|
||||||
},
|
|
||||||
"startedDateTime": "2021-01-14T14:10:40.611Z",
|
|
||||||
"time": 25,
|
|
||||||
"timings": {
|
|
||||||
"blocked": -1,
|
|
||||||
"connect": -1,
|
|
||||||
"dns": -1,
|
|
||||||
"receive": 0,
|
|
||||||
"send": 0,
|
|
||||||
"ssl": -1,
|
|
||||||
"wait": 25
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pages": [],
|
|
||||||
"version": "1.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
{
|
|
||||||
"log": {
|
|
||||||
"_recordingName": "User/will not be logged if is non-staff",
|
|
||||||
"creator": {
|
|
||||||
"comment": "persister:fs",
|
|
||||||
"name": "Polly.JS",
|
|
||||||
"version": "5.0.0"
|
|
||||||
},
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"_id": "0b09ec35ecae5b17a2ccda062b1d6ef5",
|
|
||||||
"_order": 0,
|
|
||||||
"cache": {},
|
|
||||||
"request": {
|
|
||||||
"bodySize": 602,
|
|
||||||
"cookies": [],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept",
|
|
||||||
"value": "*/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "602"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "user-agent",
|
|
||||||
"value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "accept-encoding",
|
|
||||||
"value": "gzip,deflate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "connection",
|
|
||||||
"value": "close"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "host",
|
|
||||||
"value": "localhost:8000"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 254,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"method": "POST",
|
|
||||||
"postData": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"params": [],
|
|
||||||
"text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"client@example.com\",\"password\":\"password\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
|
||||||
},
|
|
||||||
"queryString": [],
|
|
||||||
"url": "http://localhost:8000/graphql/"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"bodySize": 616,
|
|
||||||
"content": {
|
|
||||||
"mimeType": "application/json",
|
|
||||||
"size": 616,
|
|
||||||
"text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"Gac5v8mZt6dW0HBXp5RNt8GAWciTbVzsycpqtUKV797npCXajke5h9VoF4l9MreP\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMDY5ODkzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiVlhObGNqb3pNQT09IiwiaXNfc3RhZmYiOmZhbHNlfQ.RVYwqQSPEZoi2E_ImC30Ml37RJ2Fu6AnSmfDkAYMcqY\", \"user\": {\"id\": \"VXNlcjozMA==\", \"email\": \"client@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": false, \"userPermissions\": [], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]"
|
|
||||||
},
|
|
||||||
"cookies": [
|
|
||||||
{
|
|
||||||
"httpOnly": true,
|
|
||||||
"name": "refreshToken",
|
|
||||||
"path": "/",
|
|
||||||
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMzI5MDYzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiR2FjNXY4bVp0NmRXMEhCWHA1Uk50OEdBV2NpVGJWenN5Y3BxdFVLVjc5N25wQ1hhamtlNWg5Vm9GNGw5TXJlUCJ9.jUF_9vvtwT8EUbQ4GM7u0YVivk7TiSoSecHDZ0jJ2MI"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"name": "date",
|
|
||||||
"value": "Fri, 15 Jan 2021 08:17:13 GMT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "server",
|
|
||||||
"value": "WSGIServer/0.2 CPython/3.8.7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "content-length",
|
|
||||||
"value": "616"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "x-content-type-options",
|
|
||||||
"value": "nosniff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "referrer-policy",
|
|
||||||
"value": "same-origin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "set-cookie",
|
|
||||||
"value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMzI5MDYzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiR2FjNXY4bVp0NmRXMEhCWHA1Uk50OEdBV2NpVGJWenN5Y3BxdFVLVjc5N25wQ1hhamtlNWg5Vm9GNGw5TXJlUCJ9.jUF_9vvtwT8EUbQ4GM7u0YVivk7TiSoSecHDZ0jJ2MI; HttpOnly; Path=/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headersSize": 619,
|
|
||||||
"httpVersion": "HTTP/1.1",
|
|
||||||
"redirectURL": "",
|
|
||||||
"status": 200,
|
|
||||||
"statusText": "OK"
|
|
||||||
},
|
|
||||||
"startedDateTime": "2021-01-15T08:17:12.850Z",
|
|
||||||
"time": 623,
|
|
||||||
"timings": {
|
|
||||||
"blocked": -1,
|
|
||||||
"connect": -1,
|
|
||||||
"dns": -1,
|
|
||||||
"receive": 0,
|
|
||||||
"send": 0,
|
|
||||||
"ssl": -1,
|
|
||||||
"wait": 623
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pages": [],
|
|
||||||
"version": "1.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,11 +8,11 @@
|
||||||
},
|
},
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"_id": "6fed086670e88883223acb33bab4ff31",
|
"_id": "f36c6d9d965b62862363338cf17d6135",
|
||||||
"_order": 0,
|
"_order": 0,
|
||||||
"cache": {},
|
"cache": {},
|
||||||
"request": {
|
"request": {
|
||||||
"bodySize": 599,
|
"bodySize": 324,
|
||||||
"cookies": [],
|
"cookies": [],
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
"name": "content-length",
|
"name": "content-length",
|
||||||
"value": "599"
|
"value": "324"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
|
@ -50,33 +50,33 @@
|
||||||
"value": "localhost:8000"
|
"value": "localhost:8000"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headersSize": 281,
|
"headersSize": 254,
|
||||||
"httpVersion": "HTTP/1.1",
|
"httpVersion": "HTTP/1.1",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"postData": {
|
"postData": {
|
||||||
"mimeType": "application/json",
|
"mimeType": "application/json",
|
||||||
"params": [],
|
"params": [],
|
||||||
"text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
"text": "[{\"operationName\":\"UserDetails\",\"variables\":{},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nquery UserDetails {\\n me {\\n ...User\\n __typename\\n }\\n}\\n\"}]"
|
||||||
},
|
},
|
||||||
"queryString": [],
|
"queryString": [],
|
||||||
"url": "http://localhost:8000/graphql/"
|
"url": "http://localhost:8000/graphql/"
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"bodySize": 214,
|
"bodySize": 24,
|
||||||
"content": {
|
"content": {
|
||||||
"mimeType": "application/json",
|
"mimeType": "application/json",
|
||||||
"size": 214,
|
"size": 24,
|
||||||
"text": "[{\"data\": {\"tokenCreate\": {\"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"Error\"}], \"csrfToken\": null, \"token\": null, \"user\": null, \"__typename\": \"CreateToken\"}}}]"
|
"text": "[{\"data\": {\"me\": null}}]"
|
||||||
},
|
},
|
||||||
"cookies": [],
|
"cookies": [],
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
"name": "date",
|
"name": "date",
|
||||||
"value": "Thu, 14 Jan 2021 13:55:45 GMT"
|
"value": "Thu, 07 Oct 2021 23:09:08 GMT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"value": "WSGIServer/0.2 CPython/3.8.7"
|
"value": "WSGIServer/0.2 CPython/3.8.3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "content-type",
|
"name": "content-type",
|
||||||
|
@ -84,7 +84,113 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "content-length",
|
"name": "content-length",
|
||||||
"value": "214"
|
"value": "24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-content-type-options",
|
||||||
|
"value": "nosniff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "referrer-policy",
|
||||||
|
"value": "same-origin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"headersSize": 192,
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"redirectURL": "",
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK"
|
||||||
|
},
|
||||||
|
"startedDateTime": "2021-10-07T23:09:08.310Z",
|
||||||
|
"time": 105,
|
||||||
|
"timings": {
|
||||||
|
"blocked": -1,
|
||||||
|
"connect": -1,
|
||||||
|
"dns": -1,
|
||||||
|
"receive": 0,
|
||||||
|
"send": 0,
|
||||||
|
"ssl": -1,
|
||||||
|
"wait": 105
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "156d01ee9c99f8b74ab79e7482eadf9f",
|
||||||
|
"_order": 0,
|
||||||
|
"cache": {},
|
||||||
|
"request": {
|
||||||
|
"bodySize": 1170,
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"_fromType": "array",
|
||||||
|
"name": "accept",
|
||||||
|
"value": "*/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_fromType": "array",
|
||||||
|
"name": "content-type",
|
||||||
|
"value": "application/json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_fromType": "array",
|
||||||
|
"name": "content-length",
|
||||||
|
"value": "1170"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_fromType": "array",
|
||||||
|
"name": "user-agent",
|
||||||
|
"value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_fromType": "array",
|
||||||
|
"name": "accept-encoding",
|
||||||
|
"value": "gzip,deflate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_fromType": "array",
|
||||||
|
"name": "connection",
|
||||||
|
"value": "close"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "host",
|
||||||
|
"value": "localhost:8000"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"headersSize": 255,
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"method": "POST",
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"params": [],
|
||||||
|
"text": "{\"operationName\":\"login\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment AccountErrorFragment on AccountError {\\n code\\n field\\n message\\n __typename\\n}\\n\\nfragment AddressFragment on Address {\\n id\\n firstName\\n lastName\\n companyName\\n streetAddress1\\n streetAddress2\\n city\\n cityArea\\n postalCode\\n country {\\n code\\n country\\n __typename\\n }\\n countryArea\\n phone\\n isDefaultBillingAddress\\n isDefaultShippingAddress\\n __typename\\n}\\n\\nfragment UserFragment on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n metadata {\\n key\\n value\\n __typename\\n }\\n defaultShippingAddress {\\n ...AddressFragment\\n __typename\\n }\\n defaultBillingAddress {\\n ...AddressFragment\\n __typename\\n }\\n addresses {\\n ...AddressFragment\\n __typename\\n }\\n __typename\\n}\\n\\nmutation login($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n csrfToken\\n token\\n errors {\\n ...AccountErrorFragment\\n __typename\\n }\\n user {\\n ...UserFragment\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}"
|
||||||
|
},
|
||||||
|
"queryString": [],
|
||||||
|
"url": "http://localhost:8000/graphql/"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"bodySize": 243,
|
||||||
|
"content": {
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"size": 243,
|
||||||
|
"text": "{\"data\": {\"tokenCreate\": {\"csrfToken\": null, \"token\": null, \"errors\": [{\"code\": \"INVALID_CREDENTIALS\", \"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"user\": null, \"__typename\": \"CreateToken\"}}}"
|
||||||
|
},
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "date",
|
||||||
|
"value": "Mon, 11 Oct 2021 11:17:53 GMT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"value": "WSGIServer/0.2 CPython/3.8.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content-type",
|
||||||
|
"value": "application/json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content-length",
|
||||||
|
"value": "243"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "x-content-type-options",
|
"name": "x-content-type-options",
|
||||||
|
@ -101,8 +207,8 @@
|
||||||
"status": 200,
|
"status": 200,
|
||||||
"statusText": "OK"
|
"statusText": "OK"
|
||||||
},
|
},
|
||||||
"startedDateTime": "2021-04-22T11:09:20.963Z",
|
"startedDateTime": "2021-10-11T11:17:53.202Z",
|
||||||
"time": 555,
|
"time": 438,
|
||||||
"timings": {
|
"timings": {
|
||||||
"blocked": -1,
|
"blocked": -1,
|
||||||
"connect": -1,
|
"connect": -1,
|
||||||
|
@ -110,7 +216,7 @@
|
||||||
"receive": 0,
|
"receive": 0,
|
||||||
"send": 0,
|
"send": 0,
|
||||||
"ssl": -1,
|
"ssl": -1,
|
||||||
"wait": 555
|
"wait": 438
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
},
|
},
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"_id": "8948f5cbe2259e56b6dd03f068fbfa4d",
|
"_id": "07fe7bf33b7e219c3280229514fcf39d",
|
||||||
"_order": 0,
|
"_order": 0,
|
||||||
"cache": {},
|
"cache": {},
|
||||||
"request": {
|
"request": {
|
||||||
"bodySize": 587,
|
"bodySize": 1158,
|
||||||
"cookies": [],
|
"cookies": [],
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
"name": "content-length",
|
"name": "content-length",
|
||||||
"value": "587"
|
"value": "1158"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_fromType": "array",
|
"_fromType": "array",
|
||||||
|
@ -50,40 +50,33 @@
|
||||||
"value": "localhost:8000"
|
"value": "localhost:8000"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headersSize": 281,
|
"headersSize": 255,
|
||||||
"httpVersion": "HTTP/1.1",
|
"httpVersion": "HTTP/1.1",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"postData": {
|
"postData": {
|
||||||
"mimeType": "application/json",
|
"mimeType": "application/json",
|
||||||
"params": [],
|
"params": [],
|
||||||
"text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"client@example.com\",\"password\":\"password\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"
|
"text": "{\"operationName\":\"login\",\"variables\":{\"email\":\"client@example.com\",\"password\":\"password\"},\"query\":\"fragment AccountErrorFragment on AccountError {\\n code\\n field\\n message\\n __typename\\n}\\n\\nfragment AddressFragment on Address {\\n id\\n firstName\\n lastName\\n companyName\\n streetAddress1\\n streetAddress2\\n city\\n cityArea\\n postalCode\\n country {\\n code\\n country\\n __typename\\n }\\n countryArea\\n phone\\n isDefaultBillingAddress\\n isDefaultShippingAddress\\n __typename\\n}\\n\\nfragment UserFragment on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n metadata {\\n key\\n value\\n __typename\\n }\\n defaultShippingAddress {\\n ...AddressFragment\\n __typename\\n }\\n defaultBillingAddress {\\n ...AddressFragment\\n __typename\\n }\\n addresses {\\n ...AddressFragment\\n __typename\\n }\\n __typename\\n}\\n\\nmutation login($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n csrfToken\\n token\\n errors {\\n ...AccountErrorFragment\\n __typename\\n }\\n user {\\n ...UserFragment\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}"
|
||||||
},
|
},
|
||||||
"queryString": [],
|
"queryString": [],
|
||||||
"url": "http://localhost:8000/graphql/"
|
"url": "http://localhost:8000/graphql/"
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"bodySize": 616,
|
"bodySize": 243,
|
||||||
"content": {
|
"content": {
|
||||||
"mimeType": "application/json",
|
"mimeType": "application/json",
|
||||||
"size": 616,
|
"size": 243,
|
||||||
"text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"ztCCq2djodzVWeaynv8ifQbIIB4nrM6HyInqcZ4xqoaIEfOOKUEhAqw9rVR8Cr8L\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMTA2NjgxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiVlhObGNqb3pNQT09IiwiaXNfc3RhZmYiOmZhbHNlfQ.lXsNnIBxZCCL843TTjn84lkWpE05o88F5q811ApjdKA\", \"user\": {\"id\": \"VXNlcjozMA==\", \"email\": \"client@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": false, \"userPermissions\": [], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]"
|
"text": "{\"data\": {\"tokenCreate\": {\"csrfToken\": null, \"token\": null, \"errors\": [{\"code\": \"INVALID_CREDENTIALS\", \"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"user\": null, \"__typename\": \"CreateToken\"}}}"
|
||||||
},
|
},
|
||||||
"cookies": [
|
"cookies": [],
|
||||||
{
|
|
||||||
"httpOnly": true,
|
|
||||||
"name": "refreshToken",
|
|
||||||
"path": "/",
|
|
||||||
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMzY1ODUxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoienRDQ3EyZGpvZHpWV2VheW52OGlmUWJJSUI0bnJNNkh5SW5xY1o0eHFvYUlFZk9PS1VFaEFxdzlyVlI4Q3I4TCJ9.hDMEK3HFSLol7rLd9dfaSCLTWgvetNDAFsb039L9PXQ"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
"name": "date",
|
"name": "date",
|
||||||
"value": "Tue, 19 Jan 2021 14:28:37 GMT"
|
"value": "Mon, 11 Oct 2021 11:17:53 GMT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"value": "WSGIServer/0.2 CPython/3.9.1"
|
"value": "WSGIServer/0.2 CPython/3.8.3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "content-type",
|
"name": "content-type",
|
||||||
|
@ -91,7 +84,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "content-length",
|
"name": "content-length",
|
||||||
"value": "616"
|
"value": "243"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "x-content-type-options",
|
"name": "x-content-type-options",
|
||||||
|
@ -100,21 +93,16 @@
|
||||||
{
|
{
|
||||||
"name": "referrer-policy",
|
"name": "referrer-policy",
|
||||||
"value": "same-origin"
|
"value": "same-origin"
|
||||||
},
|
|
||||||
{
|
|
||||||
"_fromType": "array",
|
|
||||||
"name": "set-cookie",
|
|
||||||
"value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMzY1ODUxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoienRDQ3EyZGpvZHpWV2VheW52OGlmUWJJSUI0bnJNNkh5SW5xY1o0eHFvYUlFZk9PS1VFaEFxdzlyVlI4Q3I4TCJ9.hDMEK3HFSLol7rLd9dfaSCLTWgvetNDAFsb039L9PXQ; HttpOnly; Path=/"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headersSize": 619,
|
"headersSize": 193,
|
||||||
"httpVersion": "HTTP/1.1",
|
"httpVersion": "HTTP/1.1",
|
||||||
"redirectURL": "",
|
"redirectURL": "",
|
||||||
"status": 200,
|
"status": 200,
|
||||||
"statusText": "OK"
|
"statusText": "OK"
|
||||||
},
|
},
|
||||||
"startedDateTime": "2021-04-22T11:09:21.540Z",
|
"startedDateTime": "2021-10-11T11:17:53.664Z",
|
||||||
"time": 382,
|
"time": 61,
|
||||||
"timings": {
|
"timings": {
|
||||||
"blocked": -1,
|
"blocked": -1,
|
||||||
"connect": -1,
|
"connect": -1,
|
||||||
|
@ -122,7 +110,7 @@
|
||||||
"receive": 0,
|
"receive": 0,
|
||||||
"send": 0,
|
"send": 0,
|
||||||
"ssl": -1,
|
"ssl": -1,
|
||||||
"wait": 382
|
"wait": 61
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
import setupApi from "@test/api";
|
|
||||||
import { act, renderHook } from "@testing-library/react-hooks";
|
|
||||||
import ApolloClient from "apollo-client";
|
|
||||||
|
|
||||||
import { useAuthProvider } from "./hooks/useAuthProvider";
|
|
||||||
import { getTokens, setAuthToken } from "./utils";
|
|
||||||
|
|
||||||
const apolloClient = setupApi();
|
|
||||||
|
|
||||||
function renderAuthProvider(apolloClient: ApolloClient<any>) {
|
|
||||||
const intl = {
|
|
||||||
formatMessage: ({ defaultMessage }) => defaultMessage
|
|
||||||
};
|
|
||||||
const notify = jest.fn();
|
|
||||||
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useAuthProvider({ apolloClient, intl: intl as any, notify })
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const adminCredentials = {
|
|
||||||
email: "admin@example.com",
|
|
||||||
password: "admin",
|
|
||||||
token: null
|
|
||||||
};
|
|
||||||
|
|
||||||
const nonStaffUserCredentials = {
|
|
||||||
email: "client@example.com",
|
|
||||||
password: "password"
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
localStorage.clear();
|
|
||||||
sessionStorage.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("User", () => {
|
|
||||||
it("will be logged in if has valid credentials", async done => {
|
|
||||||
const hook = renderAuthProvider(apolloClient);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await hook.current.login(
|
|
||||||
adminCredentials.email,
|
|
||||||
adminCredentials.password
|
|
||||||
);
|
|
||||||
});
|
|
||||||
expect(hook.current.user.email).toBe(adminCredentials.email);
|
|
||||||
adminCredentials.token = getTokens().auth;
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("will not be logged in if doesn't have valid credentials", async done => {
|
|
||||||
const hook = renderAuthProvider(apolloClient);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await hook.current.login(adminCredentials.email, "NotAValidPassword123!");
|
|
||||||
});
|
|
||||||
expect(hook.current.user).toBe(null);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("will not be logged in if is non-staff", async done => {
|
|
||||||
const hook = renderAuthProvider(apolloClient);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await hook.current.login(
|
|
||||||
nonStaffUserCredentials.email,
|
|
||||||
nonStaffUserCredentials.password
|
|
||||||
);
|
|
||||||
});
|
|
||||||
expect(hook.current.user).toBe(undefined);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("will be logged if has valid token", async done => {
|
|
||||||
setAuthToken(adminCredentials.token, false);
|
|
||||||
const hook = renderAuthProvider(apolloClient);
|
|
||||||
|
|
||||||
await act(() => hook.current.autologinPromise.current);
|
|
||||||
expect(hook.current.user.email).toBe(adminCredentials.email);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("will not be logged if has invalid token", async done => {
|
|
||||||
setAuthToken("NotAToken", false);
|
|
||||||
const hook = renderAuthProvider(apolloClient);
|
|
||||||
|
|
||||||
await act(() => hook.current.autologinPromise.current);
|
|
||||||
expect(hook.current.user).toBe(undefined);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
92
src/auth/AuthProvider.test.tsx
Normal file
92
src/auth/AuthProvider.test.tsx
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import { createSaleorClient, SaleorProvider } from "@saleor/sdk";
|
||||||
|
import setupApi from "@test/api";
|
||||||
|
import { act, renderHook } from "@testing-library/react-hooks";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useAuthProvider } from "./hooks/useAuthProvider";
|
||||||
|
|
||||||
|
const apolloClient = setupApi();
|
||||||
|
|
||||||
|
function renderAuthProvider() {
|
||||||
|
const intl = {
|
||||||
|
formatMessage: ({ defaultMessage }) => defaultMessage
|
||||||
|
};
|
||||||
|
const notify = jest.fn();
|
||||||
|
const saleorClient = createSaleorClient({
|
||||||
|
apiUrl: process.env.API_URI || "http://localhost:8000/graphql/",
|
||||||
|
channel: ""
|
||||||
|
});
|
||||||
|
const wrapper = ({ children }) => (
|
||||||
|
<SaleorProvider client={saleorClient}>{children}</SaleorProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => useAuthProvider({ intl: intl as any, notify, apolloClient }),
|
||||||
|
{ wrapper }
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminCredentials = {
|
||||||
|
email: "admin@example.com",
|
||||||
|
password: "admin",
|
||||||
|
token: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const nonStaffUserCredentials = {
|
||||||
|
email: "client@example.com",
|
||||||
|
password: "password"
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
sessionStorage.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("User", () => {
|
||||||
|
it("will be logged in if has valid credentials", async done => {
|
||||||
|
const hook = renderAuthProvider();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const result = await hook.current.login(
|
||||||
|
adminCredentials.email,
|
||||||
|
adminCredentials.password
|
||||||
|
);
|
||||||
|
expect(result.user?.email).toBe(adminCredentials.email);
|
||||||
|
});
|
||||||
|
expect(hook.current.authenticated).toBe(true);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("will not be logged in if doesn't have valid credentials", async done => {
|
||||||
|
const hook = renderAuthProvider();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const result = await hook.current.login(
|
||||||
|
adminCredentials.email,
|
||||||
|
"NotAValidPassword123!"
|
||||||
|
);
|
||||||
|
expect(result.user).toBe(null);
|
||||||
|
});
|
||||||
|
expect(hook.current.authenticated).toBe(false);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("will not be logged in if is non-staff", async done => {
|
||||||
|
const hook = renderAuthProvider();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const result = await hook.current.login(
|
||||||
|
nonStaffUserCredentials.email,
|
||||||
|
nonStaffUserCredentials.password
|
||||||
|
);
|
||||||
|
expect(result.user).toBe(null);
|
||||||
|
});
|
||||||
|
expect(hook.current.authenticated).toBe(false);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,11 +1,10 @@
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import React, { useContext } from "react";
|
import React from "react";
|
||||||
import { useApolloClient } from "react-apollo";
|
import { useApolloClient } from "react-apollo";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { UserContext } from "./";
|
import { UserContext } from "./";
|
||||||
import { useAuthProvider } from "./hooks/useAuthProvider";
|
import { useAuthProvider } from "./hooks/useAuthProvider";
|
||||||
import { getTokens } from "./utils";
|
|
||||||
|
|
||||||
interface AuthProviderProps {
|
interface AuthProviderProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -16,24 +15,11 @@ const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
|
|
||||||
const authProvider = useAuthProvider({ apolloClient, intl, notify });
|
const authProvider = useAuthProvider({ intl, notify, apolloClient });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserContext.Provider value={authProvider}>{children}</UserContext.Provider>
|
<UserContext.Provider value={authProvider}>{children}</UserContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAuth = () => {
|
|
||||||
const user = useContext(UserContext);
|
|
||||||
const isAuthenticated = !!user.user;
|
|
||||||
|
|
||||||
return {
|
|
||||||
hasToken: !!getTokens().auth,
|
|
||||||
isAuthenticated,
|
|
||||||
tokenAuthLoading: user.tokenAuthLoading,
|
|
||||||
tokenVerifyLoading: user.tokenVerifyLoading,
|
|
||||||
user: user.user
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AuthProvider;
|
export default AuthProvider;
|
||||||
|
|
|
@ -7,7 +7,6 @@ import LoginPage, { LoginCardProps } from "../../../auth/components/LoginPage";
|
||||||
|
|
||||||
const props: Omit<LoginCardProps, "classes"> = {
|
const props: Omit<LoginCardProps, "classes"> = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
error: false,
|
|
||||||
externalAuthentications: [
|
externalAuthentications: [
|
||||||
{
|
{
|
||||||
__typename: "ExternalAuthentication",
|
__typename: "ExternalAuthentication",
|
||||||
|
@ -15,7 +14,6 @@ const props: Omit<LoginCardProps, "classes"> = {
|
||||||
name: "Example auth plugin"
|
name: "Example auth plugin"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
externalError: false,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
onExternalAuthentication: () => undefined,
|
onExternalAuthentication: () => undefined,
|
||||||
onPasswordRecovery: undefined,
|
onPasswordRecovery: undefined,
|
||||||
|
@ -26,6 +24,9 @@ storiesOf("Views / Authentication / Log in", module)
|
||||||
.addDecorator(CardDecorator)
|
.addDecorator(CardDecorator)
|
||||||
.addDecorator(Decorator)
|
.addDecorator(Decorator)
|
||||||
.add("default", () => <LoginPage {...props} />)
|
.add("default", () => <LoginPage {...props} />)
|
||||||
.add("error", () => <LoginPage {...props} error={true} />)
|
.add("error login", () => <LoginPage {...props} error={"loginError"} />)
|
||||||
|
.add("error external login", () => (
|
||||||
|
<LoginPage {...props} error={"externalLoginError"} />
|
||||||
|
))
|
||||||
.add("disabled", () => <LoginPage {...props} disabled={true} />)
|
.add("disabled", () => <LoginPage {...props} disabled={true} />)
|
||||||
.add("loading", () => <LoginPage {...props} loading={true} />);
|
.add("loading", () => <LoginPage {...props} loading={true} />);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
Typography
|
Typography
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import { UserContextError } from "@saleor/auth/types";
|
||||||
import { AvailableExternalAuthentications_shop_availableExternalAuthentications } from "@saleor/auth/types/AvailableExternalAuthentications";
|
import { AvailableExternalAuthentications_shop_availableExternalAuthentications } from "@saleor/auth/types/AvailableExternalAuthentications";
|
||||||
import { FormSpacer } from "@saleor/components/FormSpacer";
|
import { FormSpacer } from "@saleor/components/FormSpacer";
|
||||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||||
|
@ -14,6 +15,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import LoginForm, { LoginFormData } from "./form";
|
import LoginForm, { LoginFormData } from "./form";
|
||||||
|
import { getErrorMessage } from "./messages";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -49,8 +51,7 @@ const useStyles = makeStyles(
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface LoginCardProps {
|
export interface LoginCardProps {
|
||||||
error: boolean;
|
error?: UserContextError;
|
||||||
externalError: boolean;
|
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
externalAuthentications?: AvailableExternalAuthentications_shop_availableExternalAuthentications[];
|
externalAuthentications?: AvailableExternalAuthentications_shop_availableExternalAuthentications[];
|
||||||
|
@ -62,7 +63,6 @@ export interface LoginCardProps {
|
||||||
const LoginCard: React.FC<LoginCardProps> = props => {
|
const LoginCard: React.FC<LoginCardProps> = props => {
|
||||||
const {
|
const {
|
||||||
error,
|
error,
|
||||||
externalError,
|
|
||||||
disabled,
|
disabled,
|
||||||
loading,
|
loading,
|
||||||
externalAuthentications = [],
|
externalAuthentications = [],
|
||||||
|
@ -84,19 +84,12 @@ const LoginCard: React.FC<LoginCardProps> = props => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoginForm onSubmit={onSubmit}>
|
<LoginForm onSubmit={onSubmit}>
|
||||||
{({ change: handleChange, data, submit: handleSubmit }) => (
|
{({ change: handleChange, data }) => (
|
||||||
<>
|
<>
|
||||||
{error && (
|
{error && (
|
||||||
<div className={classes.panel} data-test="loginErrorMessage">
|
<div className={classes.panel} data-test="loginErrorMessage">
|
||||||
<Typography variant="caption">
|
<Typography variant="caption">
|
||||||
<FormattedMessage defaultMessage="Sorry, your username and/or password are incorrect. Please try again." />
|
{getErrorMessage(error, intl)}
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{externalError && (
|
|
||||||
<div className={classes.panel} data-test="loginErrorMessage">
|
|
||||||
<Typography variant="caption">
|
|
||||||
<FormattedMessage defaultMessage="Sorry, login went wrong. Please try again." />
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -136,7 +129,6 @@ const LoginCard: React.FC<LoginCardProps> = props => {
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleSubmit}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
data-test="submit"
|
data-test="submit"
|
||||||
>
|
>
|
||||||
|
@ -184,7 +176,6 @@ const LoginCard: React.FC<LoginCardProps> = props => {
|
||||||
color="primary"
|
color="primary"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
size="large"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onExternalAuthentication(externalAuthentication.id)
|
onExternalAuthentication(externalAuthentication.id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export interface UseLoginFormResult {
|
||||||
change: FormChange;
|
change: FormChange;
|
||||||
data: LoginFormData;
|
data: LoginFormData;
|
||||||
hasChanged: boolean;
|
hasChanged: boolean;
|
||||||
submit: () => Promise<boolean>;
|
submit: (event: React.FormEvent<HTMLFormElement>) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginFormProps {
|
export interface LoginFormProps {
|
||||||
|
@ -53,7 +53,11 @@ function useLoginForm(
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = async () => handleFormSubmit(data, handleSubmit, setChanged);
|
const submit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
return handleFormSubmit(data, handleSubmit, setChanged);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
change: handleChange,
|
change: handleChange,
|
||||||
|
|
33
src/auth/components/LoginPage/messages.ts
Normal file
33
src/auth/components/LoginPage/messages.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { UserContextError } from "@saleor/auth/types";
|
||||||
|
import { defineMessages, IntlShape } from "react-intl";
|
||||||
|
|
||||||
|
export const errorMessages = defineMessages({
|
||||||
|
loginError: {
|
||||||
|
defaultMessage:
|
||||||
|
"Sorry, your username and/or password are incorrect. Please try again.",
|
||||||
|
description: "error message"
|
||||||
|
},
|
||||||
|
externalLoginError: {
|
||||||
|
defaultMessage: "Sorry, login went wrong. Please try again.",
|
||||||
|
description: "error message"
|
||||||
|
},
|
||||||
|
serverError: {
|
||||||
|
defaultMessage:
|
||||||
|
"Saleor is unavailable, please check your network connection and try again.",
|
||||||
|
description: "error message"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function getErrorMessage(
|
||||||
|
err: UserContextError,
|
||||||
|
intl: IntlShape
|
||||||
|
): string {
|
||||||
|
switch (err) {
|
||||||
|
case "loginError":
|
||||||
|
return intl.formatMessage(errorMessages.loginError);
|
||||||
|
case "externalLoginError":
|
||||||
|
return intl.formatMessage(errorMessages.externalLoginError);
|
||||||
|
case "serverError":
|
||||||
|
return intl.formatMessage(errorMessages.serverError);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,8 @@ storiesOf("Views / Authentication / Set up a new password", module)
|
||||||
__typename: "AccountError",
|
__typename: "AccountError",
|
||||||
code: AccountErrorCode.PASSWORD_TOO_SHORT,
|
code: AccountErrorCode.PASSWORD_TOO_SHORT,
|
||||||
field,
|
field,
|
||||||
addressType: null
|
addressType: null,
|
||||||
|
message: null
|
||||||
}))}
|
}))}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Button, TextField, Typography } from "@material-ui/core";
|
import { Button, TextField, Typography } from "@material-ui/core";
|
||||||
import { SetPassword_setPassword_errors } from "@saleor/auth/types/SetPassword";
|
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import { makeStyles } from "@saleor/macaw-ui";
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
|
import { SetPasswordData } from "@saleor/sdk";
|
||||||
import getAccountErrorMessage from "@saleor/utils/errors/account";
|
import getAccountErrorMessage from "@saleor/utils/errors/account";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
@ -33,7 +33,7 @@ export interface NewPasswordPageFormData {
|
||||||
}
|
}
|
||||||
export interface NewPasswordPageProps {
|
export interface NewPasswordPageProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
errors: SetPassword_setPassword_errors[];
|
errors: SetPasswordData["errors"];
|
||||||
onSubmit: (data: NewPasswordPageFormData) => void;
|
onSubmit: (data: NewPasswordPageFormData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Route, RouteProps } from "react-router-dom";
|
import { Route, RouteProps } from "react-router-dom";
|
||||||
|
|
||||||
|
import { useUser } from "..";
|
||||||
import NotFound from "../../NotFound";
|
import NotFound from "../../NotFound";
|
||||||
import { PermissionEnum } from "../../types/globalTypes";
|
import { PermissionEnum } from "../../types/globalTypes";
|
||||||
import { hasAllPermissions, hasAnyPermissions } from "../misc";
|
import { hasAllPermissions, hasAnyPermissions } from "../misc";
|
||||||
|
|
|
@ -1,58 +1,181 @@
|
||||||
import { IMessageContext } from "@saleor/components/messages";
|
import { IMessageContext } from "@saleor/components/messages";
|
||||||
import { User } from "@saleor/fragments/types/User";
|
import { APP_DEFAULT_URI, APP_MOUNT_URI, DEMO_MODE } from "@saleor/config";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
import { commonMessages } from "@saleor/intl";
|
||||||
|
import {
|
||||||
|
GetExternalAccessTokenData,
|
||||||
|
LoginData,
|
||||||
|
useAuth,
|
||||||
|
useAuthState
|
||||||
|
} from "@saleor/sdk";
|
||||||
|
import {
|
||||||
|
isSupported as isCredentialsManagementAPISupported,
|
||||||
|
login as loginWithCredentialsManagementAPI,
|
||||||
|
saveCredentials
|
||||||
|
} from "@saleor/utils/credentialsManagement";
|
||||||
import ApolloClient from "apollo-client";
|
import ApolloClient from "apollo-client";
|
||||||
import { MutableRefObject } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { useQuery } from "react-apollo";
|
||||||
import { IntlShape } from "react-intl";
|
import { IntlShape } from "react-intl";
|
||||||
|
import urlJoin from "url-join";
|
||||||
|
|
||||||
import { useExternalAuthProvider } from "./useExternalAuthProvider";
|
import { userDetailsQuery } from "../queries";
|
||||||
import { useSaleorAuthProvider } from "./useSaleorAuthProvider";
|
import {
|
||||||
|
ExternalLoginInput,
|
||||||
|
RequestExternalLoginInput,
|
||||||
|
RequestExternalLogoutInput,
|
||||||
|
UserContext,
|
||||||
|
UserContextError
|
||||||
|
} from "../types";
|
||||||
|
import { UserDetails } from "../types/UserDetails";
|
||||||
|
import { displayDemoMessage } from "../utils";
|
||||||
|
|
||||||
export interface UseAuthProvider {
|
|
||||||
logout: () => void;
|
|
||||||
tokenAuthLoading: boolean;
|
|
||||||
tokenRefresh: () => Promise<boolean>;
|
|
||||||
tokenVerifyLoading: boolean;
|
|
||||||
user?: User;
|
|
||||||
autologinPromise?: MutableRefObject<Promise<any>>;
|
|
||||||
}
|
|
||||||
export interface UseAuthProviderOpts {
|
export interface UseAuthProviderOpts {
|
||||||
intl: IntlShape;
|
intl: IntlShape;
|
||||||
notify: IMessageContext;
|
notify: IMessageContext;
|
||||||
apolloClient: ApolloClient<any>;
|
apolloClient: ApolloClient<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthProvider(opts: UseAuthProviderOpts) {
|
export function useAuthProvider({
|
||||||
const [authPlugin, setAuthPlugin] = useLocalStorage("authPlugin", undefined);
|
intl,
|
||||||
|
notify,
|
||||||
|
apolloClient
|
||||||
|
}: UseAuthProviderOpts): UserContext {
|
||||||
|
const {
|
||||||
|
login,
|
||||||
|
getExternalAuthUrl,
|
||||||
|
getExternalAccessToken,
|
||||||
|
logout
|
||||||
|
} = useAuth();
|
||||||
|
const { authenticated, authenticating, user } = useAuthState();
|
||||||
|
const [error, setError] = useState<UserContextError>();
|
||||||
|
|
||||||
const saleorAuth = useSaleorAuthProvider({
|
useEffect(() => {
|
||||||
authPlugin,
|
if (authenticating && error) {
|
||||||
setAuthPlugin,
|
setError(undefined);
|
||||||
...opts
|
}
|
||||||
|
}, [authenticating]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!authenticated && !authenticating) {
|
||||||
|
loginWithCredentialsManagementAPI(handleLogin);
|
||||||
|
}
|
||||||
|
}, [authenticated, authenticating]);
|
||||||
|
|
||||||
|
const userDetails = useQuery<UserDetails>(userDetailsQuery, {
|
||||||
|
client: apolloClient,
|
||||||
|
skip: !authenticated
|
||||||
});
|
});
|
||||||
|
|
||||||
const externalAuth = useExternalAuthProvider({
|
const handleLogout = async () => {
|
||||||
authPlugin,
|
const result = await logout({
|
||||||
setAuthPlugin,
|
input: JSON.stringify({
|
||||||
...opts
|
returnTo: urlJoin(
|
||||||
});
|
window.location.origin,
|
||||||
|
APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI
|
||||||
|
)
|
||||||
|
} as RequestExternalLogoutInput)
|
||||||
|
});
|
||||||
|
|
||||||
const loginAuth = {
|
if (isCredentialsManagementAPISupported) {
|
||||||
login: saleorAuth.login,
|
navigator.credentials.preventSilentAccess();
|
||||||
loginByExternalPlugin: externalAuth.loginByExternalPlugin,
|
}
|
||||||
loginByToken: saleorAuth.loginByToken,
|
|
||||||
requestLoginByExternalPlugin: externalAuth.requestLoginByExternalPlugin
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = result.errors || [];
|
||||||
|
const logoutUrl = JSON.parse(
|
||||||
|
result.data?.externalLogout?.logoutData || null
|
||||||
|
)?.logoutUrl;
|
||||||
|
if (!errors.length && logoutUrl) {
|
||||||
|
window.location.href = logoutUrl;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (authPlugin) {
|
const handleLogin = async (email: string, password: string) => {
|
||||||
return {
|
try {
|
||||||
...externalAuth,
|
const result = await login({
|
||||||
...loginAuth
|
email,
|
||||||
};
|
password
|
||||||
}
|
});
|
||||||
|
|
||||||
|
if (result && !result.data.tokenCreate.errors.length) {
|
||||||
|
if (DEMO_MODE) {
|
||||||
|
displayDemoMessage(intl, notify);
|
||||||
|
}
|
||||||
|
saveCredentials(result.data.tokenCreate.user, password);
|
||||||
|
} else {
|
||||||
|
setError("loginError");
|
||||||
|
}
|
||||||
|
|
||||||
|
await logoutNonStaffUser(result.data.tokenCreate);
|
||||||
|
|
||||||
|
return result.data.tokenCreate;
|
||||||
|
} catch (error) {
|
||||||
|
setError("serverError");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRequestExternalLogin = async (
|
||||||
|
pluginId: string,
|
||||||
|
input: RequestExternalLoginInput
|
||||||
|
) => {
|
||||||
|
const result = await getExternalAuthUrl({
|
||||||
|
pluginId,
|
||||||
|
input: JSON.stringify(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
return result?.data?.externalAuthenticationUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExternalLogin = async (
|
||||||
|
pluginId: string,
|
||||||
|
input: ExternalLoginInput
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const result = await getExternalAccessToken({
|
||||||
|
pluginId,
|
||||||
|
input: JSON.stringify(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && !result.data?.externalObtainAccessTokens.errors.length) {
|
||||||
|
if (DEMO_MODE) {
|
||||||
|
displayDemoMessage(intl, notify);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("externalLoginError");
|
||||||
|
}
|
||||||
|
|
||||||
|
await logoutNonStaffUser(result.data.externalObtainAccessTokens);
|
||||||
|
|
||||||
|
return result?.data?.externalObtainAccessTokens;
|
||||||
|
} catch (error) {
|
||||||
|
setError("serverError");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoutNonStaffUser = async (
|
||||||
|
data: LoginData | GetExternalAccessTokenData
|
||||||
|
) => {
|
||||||
|
if (data.user && !data.user.isStaff) {
|
||||||
|
notify({
|
||||||
|
status: "error",
|
||||||
|
text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess),
|
||||||
|
title: intl.formatMessage(commonMessages.insufficientPermissions)
|
||||||
|
});
|
||||||
|
await handleLogout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...saleorAuth,
|
login: handleLogin,
|
||||||
...loginAuth
|
requestLoginByExternalPlugin: handleRequestExternalLogin,
|
||||||
|
loginByExternalPlugin: handleExternalLogin,
|
||||||
|
logout: handleLogout,
|
||||||
|
authenticating: authenticating && !error,
|
||||||
|
authenticated: authenticated && user?.isStaff,
|
||||||
|
user: userDetails.data?.me,
|
||||||
|
error
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,261 +0,0 @@
|
||||||
import { DEMO_MODE } from "@saleor/config";
|
|
||||||
import { User } from "@saleor/fragments/types/User";
|
|
||||||
import { commonMessages } from "@saleor/intl";
|
|
||||||
import { getFullName, getMutationStatus } from "@saleor/misc";
|
|
||||||
import errorTracker from "@saleor/services/errorTracking";
|
|
||||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
|
|
||||||
import { useMutation } from "react-apollo";
|
|
||||||
|
|
||||||
import {
|
|
||||||
externalAuthenticationUrlMutation,
|
|
||||||
externalObtainAccessTokensMutation,
|
|
||||||
externalTokenRefreshMutation,
|
|
||||||
externalTokenVerifyMutation
|
|
||||||
} from "../mutations";
|
|
||||||
import {
|
|
||||||
ExternalAuthenticationUrl,
|
|
||||||
ExternalAuthenticationUrlVariables
|
|
||||||
} from "../types/ExternalAuthenticationUrl";
|
|
||||||
import {
|
|
||||||
ExternalObtainAccessTokens,
|
|
||||||
ExternalObtainAccessTokens_externalObtainAccessTokens,
|
|
||||||
ExternalObtainAccessTokensVariables
|
|
||||||
} from "../types/ExternalObtainAccessTokens";
|
|
||||||
import {
|
|
||||||
ExternalRefreshToken,
|
|
||||||
ExternalRefreshTokenVariables
|
|
||||||
} from "../types/ExternalRefreshToken";
|
|
||||||
import {
|
|
||||||
ExternalVerifyToken,
|
|
||||||
ExternalVerifyTokenVariables
|
|
||||||
} from "../types/ExternalVerifyToken";
|
|
||||||
import {
|
|
||||||
displayDemoMessage,
|
|
||||||
getTokens,
|
|
||||||
removeTokens,
|
|
||||||
setAuthToken,
|
|
||||||
setTokens
|
|
||||||
} from "../utils";
|
|
||||||
import { UseAuthProvider, UseAuthProviderOpts } from "./useAuthProvider";
|
|
||||||
|
|
||||||
export interface RequestExternalLoginInput {
|
|
||||||
redirectUri: string;
|
|
||||||
}
|
|
||||||
export interface ExternalLoginInput {
|
|
||||||
code: string;
|
|
||||||
state: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UseExternalAuthProvider extends UseAuthProvider {
|
|
||||||
requestLoginByExternalPlugin: (
|
|
||||||
pluginId: string,
|
|
||||||
input: RequestExternalLoginInput
|
|
||||||
) => Promise<void>;
|
|
||||||
loginByExternalPlugin: (
|
|
||||||
input: ExternalLoginInput
|
|
||||||
) => Promise<ExternalObtainAccessTokens_externalObtainAccessTokens>;
|
|
||||||
}
|
|
||||||
export interface UseExternalAuthProviderOpts extends UseAuthProviderOpts {
|
|
||||||
setAuthPlugin: Dispatch<SetStateAction<any>>;
|
|
||||||
authPlugin: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const persistToken = false;
|
|
||||||
|
|
||||||
export function useExternalAuthProvider({
|
|
||||||
apolloClient,
|
|
||||||
authPlugin,
|
|
||||||
intl,
|
|
||||||
notify,
|
|
||||||
setAuthPlugin
|
|
||||||
}: UseExternalAuthProviderOpts): UseExternalAuthProvider {
|
|
||||||
const [userContext, setUserContext] = useState<undefined | User>(undefined);
|
|
||||||
const autologinPromise = useRef<Promise<any>>();
|
|
||||||
const refreshPromise = useRef<Promise<boolean>>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const token = getTokens().auth;
|
|
||||||
if (authPlugin && !!token && !userContext) {
|
|
||||||
const input = JSON.stringify({
|
|
||||||
token
|
|
||||||
});
|
|
||||||
autologinPromise.current = tokenVerify({
|
|
||||||
variables: { input, pluginId: authPlugin }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (authPlugin && userContext) {
|
|
||||||
const { id, email } = userContext;
|
|
||||||
errorTracker.setUserData({
|
|
||||||
email,
|
|
||||||
id,
|
|
||||||
username: getFullName(userContext)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!userContext.isStaff) {
|
|
||||||
logout();
|
|
||||||
notify({
|
|
||||||
status: "error",
|
|
||||||
text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess),
|
|
||||||
title: intl.formatMessage(commonMessages.insufficientPermissions)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [userContext]);
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
setUserContext(undefined);
|
|
||||||
setAuthPlugin(undefined);
|
|
||||||
removeTokens();
|
|
||||||
};
|
|
||||||
|
|
||||||
const [externalAuthenticationUrl] = useMutation<
|
|
||||||
ExternalAuthenticationUrl,
|
|
||||||
ExternalAuthenticationUrlVariables
|
|
||||||
>(externalAuthenticationUrlMutation, {
|
|
||||||
client: apolloClient,
|
|
||||||
onError: logout
|
|
||||||
});
|
|
||||||
const [obtainAccessTokens, obtainAccessTokensResult] = useMutation<
|
|
||||||
ExternalObtainAccessTokens,
|
|
||||||
ExternalObtainAccessTokensVariables
|
|
||||||
>(externalObtainAccessTokensMutation, {
|
|
||||||
client: apolloClient,
|
|
||||||
onCompleted: ({ externalObtainAccessTokens }) => {
|
|
||||||
if (externalObtainAccessTokens.errors.length > 0) {
|
|
||||||
logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = externalObtainAccessTokens.user;
|
|
||||||
|
|
||||||
setUserContext(user);
|
|
||||||
if (user) {
|
|
||||||
setTokens(
|
|
||||||
externalObtainAccessTokens.token,
|
|
||||||
externalObtainAccessTokens.csrfToken,
|
|
||||||
persistToken
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: logout
|
|
||||||
});
|
|
||||||
const [tokenRefresh] = useMutation<
|
|
||||||
ExternalRefreshToken,
|
|
||||||
ExternalRefreshTokenVariables
|
|
||||||
>(externalTokenRefreshMutation, {
|
|
||||||
client: apolloClient,
|
|
||||||
onError: logout
|
|
||||||
});
|
|
||||||
const [tokenVerify, tokenVerifyResult] = useMutation<
|
|
||||||
ExternalVerifyToken,
|
|
||||||
ExternalVerifyTokenVariables
|
|
||||||
>(externalTokenVerifyMutation, {
|
|
||||||
client: apolloClient,
|
|
||||||
onCompleted: result => {
|
|
||||||
if (result.externalVerify === null) {
|
|
||||||
logout();
|
|
||||||
} else {
|
|
||||||
const user = result.externalVerify?.user;
|
|
||||||
|
|
||||||
if (!!user) {
|
|
||||||
setUserContext(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: logout
|
|
||||||
});
|
|
||||||
|
|
||||||
const obtainAccessTokensOpts = {
|
|
||||||
...obtainAccessTokensResult,
|
|
||||||
status: getMutationStatus(obtainAccessTokensResult)
|
|
||||||
};
|
|
||||||
const tokenVerifyOpts = {
|
|
||||||
...tokenVerifyResult,
|
|
||||||
status: getMutationStatus(tokenVerifyResult)
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLogin = () => {
|
|
||||||
if (DEMO_MODE) {
|
|
||||||
displayDemoMessage(intl, notify);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const requestLoginByExternalPlugin = async (
|
|
||||||
pluginId: string,
|
|
||||||
pluginInput: RequestExternalLoginInput
|
|
||||||
) => {
|
|
||||||
const input = JSON.stringify(pluginInput);
|
|
||||||
const result = await externalAuthenticationUrl({
|
|
||||||
variables: {
|
|
||||||
input,
|
|
||||||
pluginId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && !result.data.externalAuthenticationUrl.errors.length) {
|
|
||||||
setAuthPlugin(pluginId);
|
|
||||||
|
|
||||||
const authenticationData = JSON.parse(
|
|
||||||
result.data.externalAuthenticationUrl.authenticationData
|
|
||||||
);
|
|
||||||
|
|
||||||
location.href = authenticationData.authorizationUrl;
|
|
||||||
} else {
|
|
||||||
setAuthPlugin(undefined);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginByExternalPlugin = async (loginInput: ExternalLoginInput) => {
|
|
||||||
const input = JSON.stringify(loginInput);
|
|
||||||
const result = await obtainAccessTokens({
|
|
||||||
variables: { input, pluginId: authPlugin }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && !result.data?.externalObtainAccessTokens?.errors?.length) {
|
|
||||||
if (!!onLogin) {
|
|
||||||
onLogin();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setAuthPlugin(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result?.data?.externalObtainAccessTokens;
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshToken = (): Promise<boolean> => {
|
|
||||||
if (!!refreshPromise.current) {
|
|
||||||
return refreshPromise.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const token = getTokens().refresh;
|
|
||||||
const input = JSON.stringify({
|
|
||||||
refreshToken: token
|
|
||||||
});
|
|
||||||
|
|
||||||
return tokenRefresh({ variables: { input, pluginId: authPlugin } }).then(
|
|
||||||
refreshData => {
|
|
||||||
if (!!refreshData.data.externalRefresh?.token) {
|
|
||||||
setAuthToken(refreshData.data.externalRefresh.token, persistToken);
|
|
||||||
return resolve(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
autologinPromise,
|
|
||||||
loginByExternalPlugin,
|
|
||||||
logout,
|
|
||||||
requestLoginByExternalPlugin,
|
|
||||||
tokenAuthLoading: obtainAccessTokensOpts.loading,
|
|
||||||
tokenRefresh: refreshToken,
|
|
||||||
tokenVerifyLoading: tokenVerifyOpts.loading,
|
|
||||||
user: userContext
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
import { DEMO_MODE } from "@saleor/config";
|
|
||||||
import { User } from "@saleor/fragments/types/User";
|
|
||||||
import { commonMessages } from "@saleor/intl";
|
|
||||||
import { getFullName, getMutationStatus } from "@saleor/misc";
|
|
||||||
import errorTracker from "@saleor/services/errorTracking";
|
|
||||||
import {
|
|
||||||
isSupported as isCredentialsManagementAPISupported,
|
|
||||||
login as loginWithCredentialsManagementAPI,
|
|
||||||
saveCredentials
|
|
||||||
} from "@saleor/utils/credentialsManagement";
|
|
||||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
|
|
||||||
import { useMutation } from "react-apollo";
|
|
||||||
|
|
||||||
import {
|
|
||||||
tokenAuthMutation,
|
|
||||||
tokenRefreshMutation,
|
|
||||||
tokenVerifyMutation
|
|
||||||
} from "../mutations";
|
|
||||||
import { RefreshToken, RefreshTokenVariables } from "../types/RefreshToken";
|
|
||||||
import {
|
|
||||||
TokenAuth,
|
|
||||||
TokenAuth_tokenCreate,
|
|
||||||
TokenAuthVariables
|
|
||||||
} from "../types/TokenAuth";
|
|
||||||
import { VerifyToken, VerifyTokenVariables } from "../types/VerifyToken";
|
|
||||||
import {
|
|
||||||
displayDemoMessage,
|
|
||||||
getTokens,
|
|
||||||
removeTokens,
|
|
||||||
setAuthToken,
|
|
||||||
setTokens
|
|
||||||
} from "../utils";
|
|
||||||
import { UseAuthProvider, UseAuthProviderOpts } from "./useAuthProvider";
|
|
||||||
|
|
||||||
export interface UseSaleorAuthProvider extends UseAuthProvider {
|
|
||||||
login: (username: string, password: string) => Promise<TokenAuth_tokenCreate>;
|
|
||||||
loginByToken: (auth: string, csrf: string, user: User) => void;
|
|
||||||
}
|
|
||||||
export interface UseSaleorAuthProviderOpts extends UseAuthProviderOpts {
|
|
||||||
setAuthPlugin: Dispatch<SetStateAction<any>>;
|
|
||||||
authPlugin: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const persistToken = false;
|
|
||||||
|
|
||||||
export function useSaleorAuthProvider({
|
|
||||||
apolloClient,
|
|
||||||
authPlugin,
|
|
||||||
intl,
|
|
||||||
notify,
|
|
||||||
setAuthPlugin
|
|
||||||
}: UseSaleorAuthProviderOpts): UseSaleorAuthProvider {
|
|
||||||
const [userContext, setUserContext] = useState<undefined | User>(undefined);
|
|
||||||
const autologinPromise = useRef<Promise<any>>();
|
|
||||||
const refreshPromise = useRef<Promise<boolean>>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const token = getTokens().auth;
|
|
||||||
if (!authPlugin && !!token && !userContext) {
|
|
||||||
autologinPromise.current = tokenVerify({ variables: { token } });
|
|
||||||
} else if (!authPlugin) {
|
|
||||||
autologinPromise.current = loginWithCredentialsManagementAPI(login);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!authPlugin && userContext) {
|
|
||||||
const { id, email } = userContext;
|
|
||||||
errorTracker.setUserData({
|
|
||||||
email,
|
|
||||||
id,
|
|
||||||
username: getFullName(userContext)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!userContext.isStaff) {
|
|
||||||
logout();
|
|
||||||
notify({
|
|
||||||
status: "error",
|
|
||||||
text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess),
|
|
||||||
title: intl.formatMessage(commonMessages.insufficientPermissions)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [userContext]);
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
setUserContext(undefined);
|
|
||||||
if (isCredentialsManagementAPISupported) {
|
|
||||||
navigator.credentials.preventSilentAccess();
|
|
||||||
}
|
|
||||||
removeTokens();
|
|
||||||
};
|
|
||||||
|
|
||||||
const [tokenAuth, tokenAuthResult] = useMutation<
|
|
||||||
TokenAuth,
|
|
||||||
TokenAuthVariables
|
|
||||||
>(tokenAuthMutation, {
|
|
||||||
client: apolloClient,
|
|
||||||
onCompleted: ({ tokenCreate }) => {
|
|
||||||
if (tokenCreate.errors.length > 0) {
|
|
||||||
logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = tokenCreate.user;
|
|
||||||
|
|
||||||
setUserContext(user);
|
|
||||||
if (user) {
|
|
||||||
setTokens(tokenCreate.token, tokenCreate.csrfToken, persistToken);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: logout
|
|
||||||
});
|
|
||||||
const [tokenRefresh] = useMutation<RefreshToken, RefreshTokenVariables>(
|
|
||||||
tokenRefreshMutation,
|
|
||||||
{
|
|
||||||
client: apolloClient,
|
|
||||||
onError: logout
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const [tokenVerify, tokenVerifyResult] = useMutation<
|
|
||||||
VerifyToken,
|
|
||||||
VerifyTokenVariables
|
|
||||||
>(tokenVerifyMutation, {
|
|
||||||
client: apolloClient,
|
|
||||||
onCompleted: result => {
|
|
||||||
if (result.tokenVerify === null) {
|
|
||||||
logout();
|
|
||||||
} else {
|
|
||||||
const user = result.tokenVerify?.user;
|
|
||||||
|
|
||||||
if (!!user) {
|
|
||||||
setUserContext(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: logout
|
|
||||||
});
|
|
||||||
|
|
||||||
const tokenAuthOpts = {
|
|
||||||
...tokenAuthResult,
|
|
||||||
status: getMutationStatus(tokenAuthResult)
|
|
||||||
};
|
|
||||||
const tokenVerifyOpts = {
|
|
||||||
...tokenVerifyResult,
|
|
||||||
status: getMutationStatus(tokenVerifyResult)
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLogin = () => {
|
|
||||||
if (DEMO_MODE) {
|
|
||||||
displayDemoMessage(intl, notify);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const login = async (email: string, password: string) => {
|
|
||||||
setAuthPlugin(undefined);
|
|
||||||
const result = await tokenAuth({ variables: { email, password } });
|
|
||||||
|
|
||||||
if (result && !result.data.tokenCreate.errors.length) {
|
|
||||||
if (!!onLogin) {
|
|
||||||
onLogin();
|
|
||||||
}
|
|
||||||
saveCredentials(result.data.tokenCreate.user, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.data.tokenCreate;
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginByToken = (auth: string, refresh: string, user: User) => {
|
|
||||||
setAuthPlugin(undefined);
|
|
||||||
setUserContext(user);
|
|
||||||
setTokens(auth, refresh, persistToken);
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshToken = (): Promise<boolean> => {
|
|
||||||
if (!!refreshPromise.current) {
|
|
||||||
return refreshPromise.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const token = getTokens().refresh;
|
|
||||||
|
|
||||||
return tokenRefresh({ variables: { token } }).then(refreshData => {
|
|
||||||
if (!!refreshData.data.tokenRefresh?.token) {
|
|
||||||
setAuthToken(refreshData.data.tokenRefresh.token, persistToken);
|
|
||||||
return resolve(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
autologinPromise,
|
|
||||||
login,
|
|
||||||
loginByToken,
|
|
||||||
logout,
|
|
||||||
tokenAuthLoading: tokenAuthOpts.loading,
|
|
||||||
tokenRefresh: refreshToken,
|
|
||||||
tokenVerifyLoading: tokenVerifyOpts.loading,
|
|
||||||
user: userContext
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,15 +1,9 @@
|
||||||
import { User } from "@saleor/fragments/types/User";
|
|
||||||
import { parse as parseQs } from "qs";
|
import { parse as parseQs } from "qs";
|
||||||
import React, { MutableRefObject } from "react";
|
import React, { useContext } from "react";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import Layout from "./components/Layout";
|
import Layout from "./components/Layout";
|
||||||
import {
|
import { UserContext as Context } from "./types";
|
||||||
ExternalLoginInput,
|
|
||||||
RequestExternalLoginInput
|
|
||||||
} from "./hooks/useExternalAuthProvider";
|
|
||||||
import { ExternalObtainAccessTokens_externalObtainAccessTokens } from "./types/ExternalObtainAccessTokens";
|
|
||||||
import { TokenAuth_tokenCreate } from "./types/TokenAuth";
|
|
||||||
import {
|
import {
|
||||||
LoginUrlQueryParams,
|
LoginUrlQueryParams,
|
||||||
newPasswordPath,
|
newPasswordPath,
|
||||||
|
@ -28,33 +22,13 @@ const LoginView: React.FC<RouteComponentProps<any>> = () => {
|
||||||
return <LoginViewComponent params={params} />;
|
return <LoginViewComponent params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface UserContext {
|
export const UserContext = React.createContext<Context>({
|
||||||
login: (username: string, password: string) => Promise<TokenAuth_tokenCreate>;
|
|
||||||
loginByExternalPlugin: (
|
|
||||||
input: ExternalLoginInput
|
|
||||||
) => Promise<ExternalObtainAccessTokens_externalObtainAccessTokens>;
|
|
||||||
loginByToken: (auth: string, csrf: string, user: User) => void;
|
|
||||||
logout: () => void;
|
|
||||||
requestLoginByExternalPlugin: (
|
|
||||||
pluginId: string,
|
|
||||||
input: RequestExternalLoginInput
|
|
||||||
) => Promise<void>;
|
|
||||||
tokenAuthLoading: boolean;
|
|
||||||
tokenRefresh: () => Promise<boolean>;
|
|
||||||
tokenVerifyLoading: boolean;
|
|
||||||
user?: User;
|
|
||||||
autologinPromise?: MutableRefObject<Promise<any>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserContext = React.createContext<UserContext>({
|
|
||||||
login: undefined,
|
login: undefined,
|
||||||
loginByExternalPlugin: undefined,
|
loginByExternalPlugin: undefined,
|
||||||
loginByToken: undefined,
|
|
||||||
logout: undefined,
|
logout: undefined,
|
||||||
requestLoginByExternalPlugin: undefined,
|
requestLoginByExternalPlugin: undefined,
|
||||||
tokenAuthLoading: false,
|
authenticating: false,
|
||||||
tokenRefresh: undefined,
|
authenticated: false
|
||||||
tokenVerifyLoading: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const AuthRouter: React.FC = () => (
|
const AuthRouter: React.FC = () => (
|
||||||
|
@ -72,3 +46,4 @@ AuthRouter.displayName = "AuthRouter";
|
||||||
export default AuthRouter;
|
export default AuthRouter;
|
||||||
|
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
|
export const useUser = () => useContext(UserContext);
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { setContext } from "apollo-link-context";
|
|
||||||
import { ErrorResponse, onError } from "apollo-link-error";
|
|
||||||
|
|
||||||
import { getTokens, removeTokens } from "./";
|
|
||||||
import { isJwtError, JWTError } from "./errors";
|
|
||||||
|
|
||||||
interface ResponseError extends ErrorResponse {
|
|
||||||
networkError?: Error & {
|
|
||||||
statusCode?: number;
|
|
||||||
bodyText?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const invalidateTokenLink = onError((error: ResponseError) => {
|
|
||||||
if (
|
|
||||||
(error.networkError && error.networkError.statusCode === 401) ||
|
|
||||||
error.graphQLErrors?.some(isJwtError)
|
|
||||||
) {
|
|
||||||
if (error.graphQLErrors[0].extensions.code !== JWTError.expired) {
|
|
||||||
removeTokens();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const tokenLink = setContext((_, context) => {
|
|
||||||
const authToken = getTokens().auth;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...context,
|
|
||||||
headers: {
|
|
||||||
...context.headers,
|
|
||||||
"Authorization-Bearer": authToken || null
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const link = invalidateTokenLink.concat(tokenLink);
|
|
||||||
|
|
||||||
export default link;
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { fragmentUser } from "@saleor/fragments/auth";
|
|
||||||
import { accountErrorFragment } from "@saleor/fragments/errors";
|
import { accountErrorFragment } from "@saleor/fragments/errors";
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
@ -7,44 +6,6 @@ import {
|
||||||
RequestPasswordReset,
|
RequestPasswordReset,
|
||||||
RequestPasswordResetVariables
|
RequestPasswordResetVariables
|
||||||
} from "./types/RequestPasswordReset";
|
} from "./types/RequestPasswordReset";
|
||||||
import { SetPassword, SetPasswordVariables } from "./types/SetPassword";
|
|
||||||
|
|
||||||
export const tokenAuthMutation = gql`
|
|
||||||
${fragmentUser}
|
|
||||||
mutation TokenAuth($email: String!, $password: String!) {
|
|
||||||
tokenCreate(email: $email, password: $password) {
|
|
||||||
errors {
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
csrfToken
|
|
||||||
token
|
|
||||||
user {
|
|
||||||
...User
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const tokenVerifyMutation = gql`
|
|
||||||
${fragmentUser}
|
|
||||||
mutation VerifyToken($token: String!) {
|
|
||||||
tokenVerify(token: $token) {
|
|
||||||
payload
|
|
||||||
user {
|
|
||||||
...User
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const tokenRefreshMutation = gql`
|
|
||||||
mutation RefreshToken($token: String!) {
|
|
||||||
tokenRefresh(csrfToken: $token) {
|
|
||||||
token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const requestPasswordReset = gql`
|
export const requestPasswordReset = gql`
|
||||||
${accountErrorFragment}
|
${accountErrorFragment}
|
||||||
|
@ -60,74 +21,3 @@ export const RequestPasswordResetMutation = TypedMutation<
|
||||||
RequestPasswordReset,
|
RequestPasswordReset,
|
||||||
RequestPasswordResetVariables
|
RequestPasswordResetVariables
|
||||||
>(requestPasswordReset);
|
>(requestPasswordReset);
|
||||||
|
|
||||||
export const setPassword = gql`
|
|
||||||
${accountErrorFragment}
|
|
||||||
${fragmentUser}
|
|
||||||
mutation SetPassword($email: String!, $password: String!, $token: String!) {
|
|
||||||
setPassword(email: $email, password: $password, token: $token) {
|
|
||||||
errors {
|
|
||||||
...AccountErrorFragment
|
|
||||||
}
|
|
||||||
csrfToken
|
|
||||||
refreshToken
|
|
||||||
token
|
|
||||||
user {
|
|
||||||
...User
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export const SetPasswordMutation = TypedMutation<
|
|
||||||
SetPassword,
|
|
||||||
SetPasswordVariables
|
|
||||||
>(setPassword);
|
|
||||||
|
|
||||||
export const externalAuthenticationUrlMutation = gql`
|
|
||||||
${accountErrorFragment}
|
|
||||||
mutation ExternalAuthenticationUrl($pluginId: String!, $input: JSONString!) {
|
|
||||||
externalAuthenticationUrl(pluginId: $pluginId, input: $input) {
|
|
||||||
authenticationData
|
|
||||||
errors {
|
|
||||||
...AccountErrorFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const externalObtainAccessTokensMutation = gql`
|
|
||||||
${accountErrorFragment}
|
|
||||||
${fragmentUser}
|
|
||||||
mutation ExternalObtainAccessTokens($pluginId: String!, $input: JSONString!) {
|
|
||||||
externalObtainAccessTokens(pluginId: $pluginId, input: $input) {
|
|
||||||
token
|
|
||||||
csrfToken
|
|
||||||
user {
|
|
||||||
...User
|
|
||||||
}
|
|
||||||
errors {
|
|
||||||
...AccountErrorFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const externalTokenRefreshMutation = gql`
|
|
||||||
mutation ExternalRefreshToken($pluginId: String!, $input: JSONString!) {
|
|
||||||
externalRefresh(pluginId: $pluginId, input: $input) {
|
|
||||||
token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const externalTokenVerifyMutation = gql`
|
|
||||||
${fragmentUser}
|
|
||||||
mutation ExternalVerifyToken($pluginId: String!, $input: JSONString!) {
|
|
||||||
externalVerify(pluginId: $pluginId, input: $input) {
|
|
||||||
verifyData
|
|
||||||
user {
|
|
||||||
...User
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fragmentUser } from "@saleor/fragments/auth";
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
export const availableExternalAuthentications = gql`
|
export const availableExternalAuthentications = gql`
|
||||||
|
@ -10,3 +11,12 @@ export const availableExternalAuthentications = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const userDetailsQuery = gql`
|
||||||
|
${fragmentUser}
|
||||||
|
query UserDetails {
|
||||||
|
me {
|
||||||
|
...User
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
41
src/auth/types.ts
Normal file
41
src/auth/types.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { User } from "@saleor/fragments/types/User";
|
||||||
|
import {
|
||||||
|
GetExternalAccessTokenData,
|
||||||
|
GetExternalAuthUrlData,
|
||||||
|
LoginData
|
||||||
|
} from "@saleor/sdk";
|
||||||
|
|
||||||
|
export interface RequestExternalLoginInput {
|
||||||
|
redirectUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExternalLoginInput {
|
||||||
|
code: string;
|
||||||
|
state: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestExternalLogoutInput {
|
||||||
|
returnTo: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserContextError =
|
||||||
|
| "loginError"
|
||||||
|
| "externalLoginError"
|
||||||
|
| "serverError";
|
||||||
|
|
||||||
|
export interface UserContext {
|
||||||
|
login: (username: string, password: string) => Promise<LoginData>;
|
||||||
|
loginByExternalPlugin: (
|
||||||
|
pluginId: string,
|
||||||
|
input: ExternalLoginInput
|
||||||
|
) => Promise<GetExternalAccessTokenData>;
|
||||||
|
logout: () => Promise<void>;
|
||||||
|
requestLoginByExternalPlugin: (
|
||||||
|
pluginId: string,
|
||||||
|
input: RequestExternalLoginInput
|
||||||
|
) => Promise<GetExternalAuthUrlData>;
|
||||||
|
user?: User;
|
||||||
|
authenticating: boolean;
|
||||||
|
authenticated: boolean;
|
||||||
|
error?: UserContextError;
|
||||||
|
}
|
36
src/auth/types/UserDetails.ts
Normal file
36
src/auth/types/UserDetails.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @generated
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { PermissionEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
|
// ====================================================
|
||||||
|
// GraphQL query operation: UserDetails
|
||||||
|
// ====================================================
|
||||||
|
|
||||||
|
export interface UserDetails_me_userPermissions {
|
||||||
|
__typename: "UserPermission";
|
||||||
|
code: PermissionEnum;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetails_me_avatar {
|
||||||
|
__typename: "Image";
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetails_me {
|
||||||
|
__typename: "User";
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
isStaff: boolean;
|
||||||
|
userPermissions: (UserDetails_me_userPermissions | null)[] | null;
|
||||||
|
avatar: UserDetails_me_avatar | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetails {
|
||||||
|
me: UserDetails_me | null;
|
||||||
|
}
|
|
@ -6,43 +6,6 @@ import { IntlShape } from "react-intl";
|
||||||
|
|
||||||
import { isJwtError, isTokenExpired } from "./errors";
|
import { isJwtError, isTokenExpired } from "./errors";
|
||||||
|
|
||||||
export enum TOKEN_STORAGE_KEY {
|
|
||||||
AUTH = "auth",
|
|
||||||
CSRF = "csrf"
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getTokens = () => ({
|
|
||||||
auth:
|
|
||||||
localStorage.getItem(TOKEN_STORAGE_KEY.AUTH) ||
|
|
||||||
sessionStorage.getItem(TOKEN_STORAGE_KEY.AUTH),
|
|
||||||
refresh:
|
|
||||||
localStorage.getItem(TOKEN_STORAGE_KEY.CSRF) ||
|
|
||||||
sessionStorage.getItem(TOKEN_STORAGE_KEY.CSRF)
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setTokens = (auth: string, csrf: string, persist: boolean) => {
|
|
||||||
if (persist) {
|
|
||||||
localStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth);
|
|
||||||
localStorage.setItem(TOKEN_STORAGE_KEY.CSRF, csrf);
|
|
||||||
} else {
|
|
||||||
sessionStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth);
|
|
||||||
sessionStorage.setItem(TOKEN_STORAGE_KEY.CSRF, csrf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setAuthToken = (auth: string, persist: boolean) => {
|
|
||||||
if (persist) {
|
|
||||||
localStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth);
|
|
||||||
} else {
|
|
||||||
sessionStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeTokens = () => {
|
|
||||||
localStorage.removeItem(TOKEN_STORAGE_KEY.AUTH);
|
|
||||||
sessionStorage.removeItem(TOKEN_STORAGE_KEY.AUTH);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const displayDemoMessage = (
|
export const displayDemoMessage = (
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
notify: UseNotifierResult
|
notify: UseNotifierResult
|
||||||
|
@ -55,23 +18,17 @@ export const displayDemoMessage = (
|
||||||
export async function handleQueryAuthError(
|
export async function handleQueryAuthError(
|
||||||
error: ApolloError,
|
error: ApolloError,
|
||||||
notify: IMessageContext,
|
notify: IMessageContext,
|
||||||
tokenRefresh: () => Promise<boolean>,
|
|
||||||
logout: () => void,
|
logout: () => void,
|
||||||
intl: IntlShape
|
intl: IntlShape
|
||||||
) {
|
) {
|
||||||
if (error.graphQLErrors.some(isJwtError)) {
|
if (error.graphQLErrors.some(isJwtError)) {
|
||||||
|
logout();
|
||||||
if (error.graphQLErrors.every(isTokenExpired)) {
|
if (error.graphQLErrors.every(isTokenExpired)) {
|
||||||
const success = await tokenRefresh();
|
notify({
|
||||||
|
status: "error",
|
||||||
if (!success) {
|
text: intl.formatMessage(commonMessages.sessionExpired)
|
||||||
logout();
|
});
|
||||||
notify({
|
|
||||||
status: "error",
|
|
||||||
text: intl.formatMessage(commonMessages.sessionExpired)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logout();
|
|
||||||
notify({
|
notify({
|
||||||
status: "error",
|
status: "error",
|
||||||
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { APP_DEFAULT_URI, APP_MOUNT_URI } from "@saleor/config";
|
import { APP_DEFAULT_URI, APP_MOUNT_URI } from "@saleor/config";
|
||||||
|
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import React, { useEffect } from "react";
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { useQuery } from "react-apollo";
|
import { useQuery } from "react-apollo";
|
||||||
import urlJoin from "url-join";
|
import urlJoin from "url-join";
|
||||||
import useRouter from "use-react-router";
|
import useRouter from "use-react-router";
|
||||||
|
|
||||||
|
import { useUser } from "..";
|
||||||
import LoginPage from "../components/LoginPage";
|
import LoginPage from "../components/LoginPage";
|
||||||
import { LoginFormData } from "../components/LoginPage/form";
|
import { LoginFormData } from "../components/LoginPage/form";
|
||||||
import { availableExternalAuthentications } from "../queries";
|
import { availableExternalAuthentications } from "../queries";
|
||||||
|
@ -27,46 +28,50 @@ const LoginView: React.FC<LoginViewProps> = ({ params }) => {
|
||||||
login,
|
login,
|
||||||
requestLoginByExternalPlugin,
|
requestLoginByExternalPlugin,
|
||||||
loginByExternalPlugin,
|
loginByExternalPlugin,
|
||||||
tokenAuthLoading
|
authenticating,
|
||||||
|
error
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const [isError, setIsError] = useState(false);
|
|
||||||
const [isExternalError, setIsExternalError] = useState(false);
|
|
||||||
const {
|
const {
|
||||||
data: externalAuthentications,
|
data: externalAuthentications,
|
||||||
loading: externalAuthenticationsLoading
|
loading: externalAuthenticationsLoading
|
||||||
} = useQuery<AvailableExternalAuthentications>(
|
} = useQuery<AvailableExternalAuthentications>(
|
||||||
availableExternalAuthentications
|
availableExternalAuthentications
|
||||||
);
|
);
|
||||||
|
const [
|
||||||
|
requestedExternalPluginId,
|
||||||
|
setRequestedExternalPluginId
|
||||||
|
] = useLocalStorage("requestedExternalPluginId", null);
|
||||||
|
|
||||||
const handleSubmit = async (data: LoginFormData) => {
|
const handleSubmit = async (data: LoginFormData) => {
|
||||||
const result = await login(data.email, data.password);
|
const result = await login(data.email, data.password);
|
||||||
const errors = result?.errors || [];
|
const errors = result?.errors || [];
|
||||||
|
|
||||||
setIsExternalError(false);
|
|
||||||
setIsError(!result || errors?.length > 0);
|
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRequestExternalAuthentication = (pluginId: string) =>
|
const handleRequestExternalAuthentication = async (pluginId: string) => {
|
||||||
requestLoginByExternalPlugin(pluginId, {
|
const result = await requestLoginByExternalPlugin(pluginId, {
|
||||||
redirectUri: urlJoin(
|
redirectUri: urlJoin(
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI,
|
APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI,
|
||||||
loginCallbackPath
|
loginCallbackPath
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
const data = JSON.parse(result?.authenticationData || "");
|
||||||
|
if (data && !result?.errors?.length) {
|
||||||
|
setRequestedExternalPluginId(pluginId);
|
||||||
|
window.location.href = data.authorizationUrl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleExternalAuthentication = async (code: string, state: string) => {
|
const handleExternalAuthentication = async (code: string, state: string) => {
|
||||||
const result = await loginByExternalPlugin({ code, state });
|
const result = await loginByExternalPlugin(requestedExternalPluginId, {
|
||||||
const errors = result?.errors || [];
|
code,
|
||||||
|
state
|
||||||
setIsError(false);
|
});
|
||||||
if (!result || errors?.length > 0) {
|
if (result && !result?.errors?.length) {
|
||||||
setIsExternalError(true);
|
|
||||||
} else {
|
|
||||||
navigate(APP_DEFAULT_URI);
|
navigate(APP_DEFAULT_URI);
|
||||||
}
|
}
|
||||||
return errors;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -80,13 +85,12 @@ const LoginView: React.FC<LoginViewProps> = ({ params }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoginPage
|
<LoginPage
|
||||||
error={isError}
|
error={error}
|
||||||
externalError={isExternalError}
|
disabled={authenticating}
|
||||||
disabled={tokenAuthLoading}
|
|
||||||
externalAuthentications={
|
externalAuthentications={
|
||||||
externalAuthentications?.shop?.availableExternalAuthentications
|
externalAuthentications?.shop?.availableExternalAuthentications
|
||||||
}
|
}
|
||||||
loading={externalAuthenticationsLoading || tokenAuthLoading}
|
loading={externalAuthenticationsLoading || authenticating}
|
||||||
onExternalAuthentication={handleRequestExternalAuthentication}
|
onExternalAuthentication={handleRequestExternalAuthentication}
|
||||||
onPasswordRecovery={() => navigate(passwordResetUrl)}
|
onPasswordRecovery={() => navigate(passwordResetUrl)}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|
|
@ -1,54 +1,49 @@
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import { SetPasswordData, useAuth } from "@saleor/sdk";
|
||||||
import { parse as parseQs } from "qs";
|
import { parse as parseQs } from "qs";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { RouteComponentProps } from "react-router";
|
import { RouteComponentProps } from "react-router";
|
||||||
|
|
||||||
import NewPasswordPage, {
|
import NewPasswordPage, {
|
||||||
NewPasswordPageFormData
|
NewPasswordPageFormData
|
||||||
} from "../components/NewPasswordPage";
|
} from "../components/NewPasswordPage";
|
||||||
import { SetPasswordMutation } from "../mutations";
|
|
||||||
import { SetPassword } from "../types/SetPassword";
|
|
||||||
import { NewPasswordUrlQueryParams } from "../urls";
|
import { NewPasswordUrlQueryParams } from "../urls";
|
||||||
|
|
||||||
const NewPassword: React.FC<RouteComponentProps> = ({ location }) => {
|
const NewPassword: React.FC<RouteComponentProps> = ({ location }) => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const { loginByToken } = useUser();
|
|
||||||
|
const { setPassword } = useAuth();
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [errors, setErrors] = useState<SetPasswordData["errors"]>([]);
|
||||||
|
|
||||||
const params: NewPasswordUrlQueryParams = parseQs(location.search.substr(1));
|
const params: NewPasswordUrlQueryParams = parseQs(location.search.substr(1));
|
||||||
|
|
||||||
const handleSetPassword = async (data: SetPassword) => {
|
const handleSubmit = async (data: NewPasswordPageFormData) => {
|
||||||
if (data.setPassword.errors.length === 0) {
|
setLoading(true);
|
||||||
loginByToken(
|
|
||||||
data.setPassword.token,
|
const result = await setPassword({
|
||||||
data.setPassword.csrfToken,
|
email: params.email,
|
||||||
data.setPassword.user
|
password: data.password,
|
||||||
);
|
token: params.token
|
||||||
|
});
|
||||||
|
|
||||||
|
const errors = result.data?.setPassword?.errors || [];
|
||||||
|
|
||||||
|
setErrors(errors);
|
||||||
|
setLoading(false);
|
||||||
|
|
||||||
|
if (!errors.length) {
|
||||||
navigate("/", { replace: true });
|
navigate("/", { replace: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SetPasswordMutation onCompleted={handleSetPassword}>
|
<NewPasswordPage
|
||||||
{(setPassword, setPasswordOpts) => {
|
errors={errors}
|
||||||
const handleSubmit = (data: NewPasswordPageFormData) =>
|
disabled={loading}
|
||||||
setPassword({
|
onSubmit={handleSubmit}
|
||||||
variables: {
|
/>
|
||||||
email: params.email,
|
|
||||||
password: data.password,
|
|
||||||
token: params.token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewPasswordPage
|
|
||||||
errors={setPasswordOpts.data?.setPassword.errors || []}
|
|
||||||
disabled={setPasswordOpts.loading}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</SetPasswordMutation>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,9 @@ import {
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Typography
|
Typography
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { makeStyles } from "@saleor/macaw-ui";
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
import { PermissionData } from "@saleor/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage";
|
import { PermissionData } from "@saleor/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { useAuth } from "@saleor/auth/AuthProvider";
|
import { useUser } from "@saleor/auth";
|
||||||
import { useBaseChannelsList } from "@saleor/channels/queries";
|
import { useBaseChannelsList } from "@saleor/channels/queries";
|
||||||
import { BaseChannels_channels } from "@saleor/channels/types/BaseChannels";
|
import { BaseChannels_channels } from "@saleor/channels/types/BaseChannels";
|
||||||
import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment";
|
import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||||
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
||||||
|
import { useSaleorConfig } from "@saleor/sdk";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
interface UseAppChannel {
|
interface UseAppChannel {
|
||||||
|
@ -38,10 +39,11 @@ const isValidChannel = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AppChannelProvider: React.FC = ({ children }) => {
|
export const AppChannelProvider: React.FC = ({ children }) => {
|
||||||
const { isAuthenticated } = useAuth();
|
const { setChannel } = useSaleorConfig();
|
||||||
|
const { authenticated } = useUser();
|
||||||
const [selectedChannel, setSelectedChannel] = useLocalStorage("channel", "");
|
const [selectedChannel, setSelectedChannel] = useLocalStorage("channel", "");
|
||||||
const { data: channelData, refetch } = useBaseChannelsList({
|
const { data: channelData, refetch } = useBaseChannelsList({
|
||||||
skip: !isAuthenticated
|
skip: !authenticated
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isPickerActive, setPickerActive] = React.useState(false);
|
const [isPickerActive, setPickerActive] = React.useState(false);
|
||||||
|
@ -54,6 +56,10 @@ export const AppChannelProvider: React.FC = ({ children }) => {
|
||||||
}
|
}
|
||||||
}, [channelData]);
|
}, [channelData]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setChannel(selectedChannel);
|
||||||
|
}, [selectedChannel]);
|
||||||
|
|
||||||
const availableChannels = channelData?.channels || [];
|
const availableChannels = channelData?.channels || [];
|
||||||
|
|
||||||
const channel =
|
const channel =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { LinearProgress, useMediaQuery } from "@material-ui/core";
|
import { LinearProgress, useMediaQuery } from "@material-ui/core";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import useAppState from "@saleor/hooks/useAppState";
|
import useAppState from "@saleor/hooks/useAppState";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import {
|
import {
|
||||||
makeStyles,
|
makeStyles,
|
||||||
SaleorTheme,
|
SaleorTheme,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Button, Card, CardContent, Typography } from "@material-ui/core";
|
import { Button, Card, CardContent, Typography } from "@material-ui/core";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Hr from "@saleor/components/Hr";
|
import Hr from "@saleor/components/Hr";
|
||||||
import RequirePermissions from "@saleor/components/RequirePermissions";
|
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
|
@ -2,7 +2,7 @@ import appleTouchIcon from "@assets/favicons/apple-touch-icon.png";
|
||||||
import favicon16 from "@assets/favicons/favicon-16x16.png";
|
import favicon16 from "@assets/favicons/favicon-16x16.png";
|
||||||
import favicon32 from "@assets/favicons/favicon-32x32.png";
|
import favicon32 from "@assets/favicons/favicon-32x32.png";
|
||||||
import safariPinnedTab from "@assets/favicons/safari-pinned-tab.svg";
|
import safariPinnedTab from "@assets/favicons/safari-pinned-tab.svg";
|
||||||
import { useAuth } from "@saleor/auth/AuthProvider";
|
import { useUser } from "@saleor/auth";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Helmet from "react-helmet";
|
import Helmet from "react-helmet";
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ type ShopContext = ShopInfo_shop;
|
||||||
export const ShopContext = React.createContext<ShopContext>(undefined);
|
export const ShopContext = React.createContext<ShopContext>(undefined);
|
||||||
|
|
||||||
export const ShopProvider: React.FC = ({ children }) => {
|
export const ShopProvider: React.FC = ({ children }) => {
|
||||||
const { isAuthenticated } = useAuth();
|
const { authenticated } = useUser();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedShopInfoQuery skip={!isAuthenticated}>
|
<TypedShopInfoQuery skip={!authenticated}>
|
||||||
{({ data }) => (
|
{({ data }) => (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { attributeListUrl } from "@saleor/attributes/urls";
|
import { attributeListUrl } from "@saleor/attributes/urls";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { channelsListUrl } from "@saleor/channels/urls";
|
import { channelsListUrl } from "@saleor/channels/urls";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import { APP_VERSION as dashboardVersion } from "@saleor/config";
|
import { APP_VERSION as dashboardVersion } from "@saleor/config";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import Attributes from "@saleor/icons/Attributes";
|
import Attributes from "@saleor/icons/Attributes";
|
||||||
import Channels from "@saleor/icons/Channels";
|
import Channels from "@saleor/icons/Channels";
|
||||||
import Navigation from "@saleor/icons/Navigation";
|
import Navigation from "@saleor/icons/Navigation";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { channelsListUrl } from "@saleor/channels/urls";
|
import { channelsListUrl } from "@saleor/channels/urls";
|
||||||
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { isJwtError } from "@saleor/auth/errors";
|
import { isJwtError } from "@saleor/auth/errors";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { getMutationStatus } from "@saleor/misc";
|
import { getMutationStatus } from "@saleor/misc";
|
||||||
|
@ -13,7 +14,6 @@ import {
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import useNotifier from "./useNotifier";
|
import useNotifier from "./useNotifier";
|
||||||
import useUser from "./useUser";
|
|
||||||
|
|
||||||
export type MutationResultWithOpts<TData> = MutationResult<TData> &
|
export type MutationResultWithOpts<TData> = MutationResult<TData> &
|
||||||
MutationResultAdditionalProps;
|
MutationResultAdditionalProps;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { handleQueryAuthError } from "@saleor/auth";
|
import { handleQueryAuthError } from "@saleor/auth";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { RequireAtLeastOne } from "@saleor/misc";
|
import { RequireAtLeastOne } from "@saleor/misc";
|
||||||
import { ApolloQueryResult, WatchQueryFetchPolicy } from "apollo-client";
|
import { ApolloQueryResult, WatchQueryFetchPolicy } from "apollo-client";
|
||||||
import { DocumentNode } from "graphql";
|
import { DocumentNode } from "graphql";
|
||||||
|
@ -11,7 +12,6 @@ import { PrefixedPermissions } from "../types/extendedTypes";
|
||||||
import { PermissionEnum } from "../types/globalTypes";
|
import { PermissionEnum } from "../types/globalTypes";
|
||||||
import useAppState from "./useAppState";
|
import useAppState from "./useAppState";
|
||||||
import useNotifier from "./useNotifier";
|
import useNotifier from "./useNotifier";
|
||||||
import useUser from "./useUser";
|
|
||||||
|
|
||||||
const getPermissionKey = (permission: string) =>
|
const getPermissionKey = (permission: string) =>
|
||||||
`PERMISSION_${permission}` as PrefixedPermissions;
|
`PERMISSION_${permission}` as PrefixedPermissions;
|
||||||
|
@ -81,14 +81,7 @@ function makeQuery<TData, TVariables>(
|
||||||
},
|
},
|
||||||
errorPolicy: "all",
|
errorPolicy: "all",
|
||||||
fetchPolicy: fetchPolicy || "cache-and-network",
|
fetchPolicy: fetchPolicy || "cache-and-network",
|
||||||
onError: error =>
|
onError: error => handleQueryAuthError(error, notify, user.logout, intl),
|
||||||
handleQueryAuthError(
|
|
||||||
error,
|
|
||||||
notify,
|
|
||||||
user.tokenRefresh,
|
|
||||||
user.logout,
|
|
||||||
intl
|
|
||||||
),
|
|
||||||
skip,
|
skip,
|
||||||
variables: variablesWithPermissions
|
variables: variablesWithPermissions
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { useContext } from "react";
|
|
||||||
|
|
||||||
import { UserContext } from "../auth";
|
|
||||||
|
|
||||||
function useUser() {
|
|
||||||
const user = useContext(UserContext);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
export default useUser;
|
|
|
@ -1,6 +1,7 @@
|
||||||
import DemoBanner from "@saleor/components/DemoBanner";
|
import DemoBanner from "@saleor/components/DemoBanner";
|
||||||
import useAppState from "@saleor/hooks/useAppState";
|
import useAppState from "@saleor/hooks/useAppState";
|
||||||
import { ThemeProvider } from "@saleor/macaw-ui";
|
import { ThemeProvider } from "@saleor/macaw-ui";
|
||||||
|
import { createFetch, createSaleorClient, SaleorProvider } from "@saleor/sdk";
|
||||||
import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory";
|
import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory";
|
||||||
import { IntrospectionFragmentMatcher } from "apollo-cache-inmemory";
|
import { IntrospectionFragmentMatcher } from "apollo-cache-inmemory";
|
||||||
import { ApolloClient } from "apollo-client";
|
import { ApolloClient } from "apollo-client";
|
||||||
|
@ -21,11 +22,10 @@ import { ExternalAppProvider } from "./apps/components/ExternalAppContext";
|
||||||
import { appsSection } from "./apps/urls";
|
import { appsSection } from "./apps/urls";
|
||||||
import AttributeSection from "./attributes";
|
import AttributeSection from "./attributes";
|
||||||
import { attributeSection } from "./attributes/urls";
|
import { attributeSection } from "./attributes/urls";
|
||||||
import Auth from "./auth";
|
import Auth, { useUser } from "./auth";
|
||||||
import AuthProvider, { useAuth } from "./auth/AuthProvider";
|
import AuthProvider from "./auth/AuthProvider";
|
||||||
import LoginLoading from "./auth/components/LoginLoading/LoginLoading";
|
import LoginLoading from "./auth/components/LoginLoading/LoginLoading";
|
||||||
import SectionRoute from "./auth/components/SectionRoute";
|
import SectionRoute from "./auth/components/SectionRoute";
|
||||||
import authLink from "./auth/link";
|
|
||||||
import CategorySection from "./categories";
|
import CategorySection from "./categories";
|
||||||
import ChannelsSection from "./channels";
|
import ChannelsSection from "./channels";
|
||||||
import { channelsSection } from "./channels/urls";
|
import { channelsSection } from "./channels/urls";
|
||||||
|
@ -83,7 +83,8 @@ errorTracker.init();
|
||||||
// so we need to explicitly set them
|
// so we need to explicitly set them
|
||||||
const linkOptions = {
|
const linkOptions = {
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
uri: API_URI
|
uri: API_URI,
|
||||||
|
fetch: createFetch()
|
||||||
};
|
};
|
||||||
const uploadLink = createUploadLink(linkOptions);
|
const uploadLink = createUploadLink(linkOptions);
|
||||||
const batchLink = new BatchHttpLink({
|
const batchLink = new BatchHttpLink({
|
||||||
|
@ -113,60 +114,57 @@ const apolloClient = new ApolloClient({
|
||||||
return defaultDataIdFromObject(obj);
|
return defaultDataIdFromObject(obj);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
link: authLink.concat(link)
|
link
|
||||||
|
});
|
||||||
|
|
||||||
|
const saleorClient = createSaleorClient({
|
||||||
|
apiUrl: API_URI,
|
||||||
|
channel: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const App: React.FC = () => (
|
const App: React.FC = () => (
|
||||||
<ApolloProvider client={apolloClient}>
|
<SaleorProvider client={saleorClient}>
|
||||||
<BrowserRouter basename={APP_MOUNT_URI}>
|
<ApolloProvider client={apolloClient}>
|
||||||
<ThemeProvider overrides={themeOverrides}>
|
<BrowserRouter basename={APP_MOUNT_URI}>
|
||||||
<DateProvider>
|
<ThemeProvider overrides={themeOverrides}>
|
||||||
<LocaleProvider>
|
<DateProvider>
|
||||||
<MessageManagerProvider>
|
<LocaleProvider>
|
||||||
<ServiceWorker />
|
<MessageManagerProvider>
|
||||||
<BackgroundTasksProvider>
|
<ServiceWorker />
|
||||||
<AppStateProvider>
|
<BackgroundTasksProvider>
|
||||||
<AuthProvider>
|
<AppStateProvider>
|
||||||
<ShopProvider>
|
<AuthProvider>
|
||||||
<AppChannelProvider>
|
<ShopProvider>
|
||||||
<ExternalAppProvider>
|
<AppChannelProvider>
|
||||||
<Routes />
|
<ExternalAppProvider>
|
||||||
</ExternalAppProvider>
|
<Routes />
|
||||||
</AppChannelProvider>
|
</ExternalAppProvider>
|
||||||
</ShopProvider>
|
</AppChannelProvider>
|
||||||
</AuthProvider>
|
</ShopProvider>
|
||||||
</AppStateProvider>
|
</AuthProvider>
|
||||||
</BackgroundTasksProvider>
|
</AppStateProvider>
|
||||||
</MessageManagerProvider>
|
</BackgroundTasksProvider>
|
||||||
</LocaleProvider>
|
</MessageManagerProvider>
|
||||||
</DateProvider>
|
</LocaleProvider>
|
||||||
</ThemeProvider>
|
</DateProvider>
|
||||||
</BrowserRouter>
|
</ThemeProvider>
|
||||||
</ApolloProvider>
|
</BrowserRouter>
|
||||||
|
</ApolloProvider>
|
||||||
|
</SaleorProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Routes: React.FC = () => {
|
const Routes: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [, dispatchAppState] = useAppState();
|
const [, dispatchAppState] = useAppState();
|
||||||
const {
|
const { authenticated, authenticating } = useUser();
|
||||||
hasToken,
|
|
||||||
isAuthenticated,
|
|
||||||
tokenAuthLoading,
|
|
||||||
tokenVerifyLoading
|
|
||||||
} = useAuth();
|
|
||||||
|
|
||||||
const { channel } = useAppChannel(false);
|
const { channel } = useAppChannel(false);
|
||||||
|
|
||||||
const channelLoaded = typeof channel !== "undefined";
|
const channelLoaded = typeof channel !== "undefined";
|
||||||
|
|
||||||
const homePageLoaded =
|
const homePageLoaded = channelLoaded && authenticated;
|
||||||
channelLoaded &&
|
|
||||||
isAuthenticated &&
|
|
||||||
!tokenAuthLoading &&
|
|
||||||
!tokenVerifyLoading;
|
|
||||||
|
|
||||||
const homePageLoading =
|
const homePageLoading = (authenticated && !channelLoaded) || authenticating;
|
||||||
(isAuthenticated && !channelLoaded) || (hasToken && tokenVerifyLoading);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -4,9 +4,9 @@ import React from "react";
|
||||||
import { Mutation, MutationFunction, MutationResult } from "react-apollo";
|
import { Mutation, MutationFunction, MutationResult } from "react-apollo";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import { useUser } from "./auth";
|
||||||
import { isJwtError } from "./auth/errors";
|
import { isJwtError } from "./auth/errors";
|
||||||
import useNotifier from "./hooks/useNotifier";
|
import useNotifier from "./hooks/useNotifier";
|
||||||
import useUser from "./hooks/useUser";
|
|
||||||
import { commonMessages } from "./intl";
|
import { commonMessages } from "./intl";
|
||||||
import { getMutationStatus } from "./misc";
|
import { getMutationStatus } from "./misc";
|
||||||
import { MutationResultAdditionalProps } from "./types";
|
import { MutationResultAdditionalProps } from "./types";
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||||
import { useCustomerAddressesQuery } from "@saleor/customers/queries";
|
import { useCustomerAddressesQuery } from "@saleor/customers/queries";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { CustomerEditData } from "@saleor/orders/components/OrderCustomer";
|
import { CustomerEditData } from "@saleor/orders/components/OrderCustomer";
|
||||||
import OrderCustomerAddressesEditDialog, {
|
import OrderCustomerAddressesEditDialog, {
|
||||||
OrderCustomerAddressesEditDialogOutput
|
OrderCustomerAddressesEditDialogOutput
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
||||||
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
||||||
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
||||||
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
||||||
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { PermissionData } from "@saleor/permissionGroups/components/PermissionGroupDetailsPage";
|
import { PermissionData } from "@saleor/permissionGroups/components/PermissionGroupDetailsPage";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import MembersErrorDialog from "@saleor/permissionGroups/components/MembersErrorDialog";
|
import MembersErrorDialog from "@saleor/permissionGroups/components/MembersErrorDialog";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -4,10 +4,9 @@ import React from "react";
|
||||||
import { Query, QueryResult } from "react-apollo";
|
import { Query, QueryResult } from "react-apollo";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { handleQueryAuthError } from "./auth";
|
import { handleQueryAuthError, useUser } from "./auth";
|
||||||
import useAppState from "./hooks/useAppState";
|
import useAppState from "./hooks/useAppState";
|
||||||
import useNotifier from "./hooks/useNotifier";
|
import useNotifier from "./hooks/useNotifier";
|
||||||
import useUser from "./hooks/useUser";
|
|
||||||
import { RequireAtLeastOne } from "./misc";
|
import { RequireAtLeastOne } from "./misc";
|
||||||
|
|
||||||
export interface LoadMore<TData, TVariables> {
|
export interface LoadMore<TData, TVariables> {
|
||||||
|
@ -79,13 +78,7 @@ export function TypedQuery<TData, TVariables>(
|
||||||
context={{ useBatching: true }}
|
context={{ useBatching: true }}
|
||||||
errorPolicy="all"
|
errorPolicy="all"
|
||||||
onError={error =>
|
onError={error =>
|
||||||
handleQueryAuthError(
|
handleQueryAuthError(error, notify, user.logout, intl)
|
||||||
error,
|
|
||||||
notify,
|
|
||||||
user.tokenRefresh,
|
|
||||||
user.logout,
|
|
||||||
intl
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(queryData: QueryResult<TData, TVariables>) => {
|
{(queryData: QueryResult<TData, TVariables>) => {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { DialogContentText, IconButton } from "@material-ui/core";
|
import { DialogContentText, IconButton } from "@material-ui/core";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import { configurationMenuUrl } from "@saleor/configuration";
|
import { configurationMenuUrl } from "@saleor/configuration";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
|
@ -11,7 +12,6 @@ import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { getStringOrPlaceholder, maybe } from "@saleor/misc";
|
import { getStringOrPlaceholder, maybe } from "@saleor/misc";
|
||||||
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { DialogContentText } from "@material-ui/core";
|
import { DialogContentText } from "@material-ui/core";
|
||||||
|
import { useUser } from "@saleor/auth";
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
|
||||||
import { commonMessages, errorMessages } from "@saleor/intl";
|
import { commonMessages, errorMessages } from "@saleor/intl";
|
||||||
import { getStringOrPlaceholder, maybe } from "@saleor/misc";
|
import { getStringOrPlaceholder, maybe } from "@saleor/misc";
|
||||||
import usePermissionGroupSearch from "@saleor/searches/usePermissionGroupSearch";
|
import usePermissionGroupSearch from "@saleor/searches/usePermissionGroupSearch";
|
||||||
|
|
|
@ -6,14 +6,12 @@ export const UserDecorator = (user: User) => storyFn => (
|
||||||
<UserContext.Provider
|
<UserContext.Provider
|
||||||
value={{
|
value={{
|
||||||
login: undefined,
|
login: undefined,
|
||||||
loginByExternalPlugin: undefined,
|
|
||||||
loginByToken: undefined,
|
|
||||||
logout: undefined,
|
|
||||||
requestLoginByExternalPlugin: undefined,
|
requestLoginByExternalPlugin: undefined,
|
||||||
tokenAuthLoading: false,
|
loginByExternalPlugin: undefined,
|
||||||
tokenRefresh: undefined,
|
logout: undefined,
|
||||||
tokenVerifyLoading: false,
|
user,
|
||||||
user
|
authenticated: false,
|
||||||
|
authenticating: false
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{storyFn()}
|
{storyFn()}
|
||||||
|
|
|
@ -36087,7 +36087,7 @@ exports[`Storyshots Views / Authentication / Log in default 1`] = `
|
||||||
class="FormSpacer-spacer-id"
|
class="FormSpacer-spacer-id"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeLarge-id MuiButton-sizeLarge-id MuiButton-fullWidth-id"
|
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-fullWidth-id"
|
||||||
data-test="external-authentication"
|
data-test="external-authentication"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -36244,7 +36244,7 @@ exports[`Storyshots Views / Authentication / Log in disabled 1`] = `
|
||||||
class="FormSpacer-spacer-id"
|
class="FormSpacer-spacer-id"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeLarge-id MuiButton-sizeLarge-id MuiButton-disabled-id MuiButton-fullWidth-id MuiButtonBase-disabled-id"
|
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-disabled-id MuiButton-fullWidth-id MuiButtonBase-disabled-id"
|
||||||
data-test="external-authentication"
|
data-test="external-authentication"
|
||||||
disabled=""
|
disabled=""
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
@ -36262,7 +36262,171 @@ exports[`Storyshots Views / Authentication / Log in disabled 1`] = `
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Storyshots Views / Authentication / Log in error 1`] = `
|
exports[`Storyshots Views / Authentication / Log in error external login 1`] = `
|
||||||
|
<div
|
||||||
|
style="padding:24px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
|
||||||
|
style="margin:auto;overflow:visible;position:relative;width:400px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiCardContent-root-id"
|
||||||
|
>
|
||||||
|
<form>
|
||||||
|
<div
|
||||||
|
class="LoginCard-panel-id"
|
||||||
|
data-test="loginErrorMessage"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root-id MuiTypography-caption-id"
|
||||||
|
>
|
||||||
|
Sorry, login went wrong. Please try again.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id"
|
||||||
|
data-shrink="false"
|
||||||
|
>
|
||||||
|
E-mail Address
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
autocomplete="username"
|
||||||
|
autofocus=""
|
||||||
|
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
|
||||||
|
data-test="email"
|
||||||
|
name="email"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-id"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
E-mail Address
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="FormSpacer-spacer-id"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root-id MuiTextField-root-id MuiFormControl-fullWidth-id"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id"
|
||||||
|
data-shrink="false"
|
||||||
|
>
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
autocomplete="password"
|
||||||
|
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
|
||||||
|
data-test="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legendLabelled-id"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Password
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="FormSpacer-spacer-id"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="LoginCard-buttonContainer-id"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id LoginCard-loginButton-id MuiButton-containedPrimary-id"
|
||||||
|
data-test="submit"
|
||||||
|
tabindex="0"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiButton-label-id"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="FormSpacer-spacer-id"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root-id MuiTypography-body1-id"
|
||||||
|
>
|
||||||
|
Forgot password?
|
||||||
|
<a
|
||||||
|
class="LoginCard-link-id"
|
||||||
|
data-test-id="reset-password-link"
|
||||||
|
>
|
||||||
|
Use this link to recover it
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="FormSpacer-spacer-id"
|
||||||
|
/>
|
||||||
|
<hr
|
||||||
|
class="MuiDivider-root-id"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="FormSpacer-spacer-id"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root-id MuiTypography-body1-id"
|
||||||
|
>
|
||||||
|
or login using
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="FormSpacer-spacer-id"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-fullWidth-id"
|
||||||
|
data-test="external-authentication"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiButton-label-id"
|
||||||
|
>
|
||||||
|
Example auth plugin
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Storyshots Views / Authentication / Log in error login 1`] = `
|
||||||
<div
|
<div
|
||||||
style="padding:24px"
|
style="padding:24px"
|
||||||
>
|
>
|
||||||
|
@ -36409,7 +36573,7 @@ exports[`Storyshots Views / Authentication / Log in error 1`] = `
|
||||||
class="FormSpacer-spacer-id"
|
class="FormSpacer-spacer-id"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeLarge-id MuiButton-sizeLarge-id MuiButton-fullWidth-id"
|
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-fullWidth-id"
|
||||||
data-test="external-authentication"
|
data-test="external-authentication"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { User } from "@saleor/fragments/types/User";
|
import { LoginData } from "@saleor/sdk";
|
||||||
|
|
||||||
export const isSupported = !!(
|
export const isSupported = !!(
|
||||||
navigator?.credentials?.preventSilentAccess && window.PasswordCredential
|
navigator?.credentials?.preventSilentAccess && window.PasswordCredential
|
||||||
|
@ -22,14 +22,13 @@ export async function login<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveCredentials(
|
export function saveCredentials(
|
||||||
user: User,
|
user: LoginData["user"],
|
||||||
password: string
|
password: string
|
||||||
): Promise<CredentialType | null> {
|
): Promise<CredentialType | null> {
|
||||||
let result: Promise<CredentialType | null>;
|
let result: Promise<CredentialType | null>;
|
||||||
|
|
||||||
if (isSupported) {
|
if (isSupported) {
|
||||||
const cred = new PasswordCredential({
|
const cred = new PasswordCredential({
|
||||||
iconURL: user.avatar ? user.avatar.url : undefined,
|
|
||||||
id: user.email,
|
id: user.email,
|
||||||
name: user.firstName ? `${user.firstName} ${user.lastName}` : undefined,
|
name: user.firstName ? `${user.firstName} ${user.lastName}` : undefined,
|
||||||
password
|
password
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment";
|
import { SetPasswordData } from "@saleor/sdk";
|
||||||
import { AccountErrorCode } from "@saleor/types/globalTypes";
|
import { AccountErrorCode } from "@saleor/types/globalTypes";
|
||||||
import { defineMessages, IntlShape } from "react-intl";
|
import { defineMessages, IntlShape } from "react-intl";
|
||||||
|
|
||||||
|
@ -31,10 +31,11 @@ const messages = defineMessages({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function getAccountErrorMessage(
|
interface ErrorFragment {
|
||||||
err: AccountErrorFragment,
|
code: AccountErrorCode | SetPasswordData["errors"][number]["code"];
|
||||||
intl: IntlShape
|
}
|
||||||
): string {
|
|
||||||
|
function getAccountErrorMessage(err: ErrorFragment, intl: IntlShape): string {
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err.code) {
|
switch (err.code) {
|
||||||
case AccountErrorCode.INVALID_PASSWORD:
|
case AccountErrorCode.INVALID_PASSWORD:
|
||||||
|
|
|
@ -17,7 +17,7 @@ type CommonErrorCode = "GRAPHQL_ERROR" | "INVALID" | "REQUIRED";
|
||||||
|
|
||||||
interface CommonError<ErrorCode> {
|
interface CommonError<ErrorCode> {
|
||||||
code: ErrorCode | CommonErrorCode;
|
code: ErrorCode | CommonErrorCode;
|
||||||
field: string | null;
|
field?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCommonFormFieldErrorMessage<ErrorCode>(
|
export function getCommonFormFieldErrorMessage<ErrorCode>(
|
||||||
|
|
|
@ -7,14 +7,7 @@ function getStaffErrorMessage(
|
||||||
err: StaffErrorFragment,
|
err: StaffErrorFragment,
|
||||||
intl: IntlShape
|
intl: IntlShape
|
||||||
): string {
|
): string {
|
||||||
return getAccountErrorMessage(
|
return getAccountErrorMessage(err, intl);
|
||||||
err && {
|
|
||||||
...err,
|
|
||||||
__typename: "AccountError",
|
|
||||||
addressType: null
|
|
||||||
},
|
|
||||||
intl
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getStaffErrorMessage;
|
export default getStaffErrorMessage;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import NodeHttpAdapter from "@pollyjs/adapter-node-http";
|
import NodeHttpAdapter from "@pollyjs/adapter-node-http";
|
||||||
import { Polly } from "@pollyjs/core";
|
import { Polly } from "@pollyjs/core";
|
||||||
import FSPersister from "@pollyjs/persister-fs";
|
import FSPersister from "@pollyjs/persister-fs";
|
||||||
|
import { createFetch } from "@saleor/sdk";
|
||||||
import { InMemoryCache } from "apollo-cache-inmemory";
|
import { InMemoryCache } from "apollo-cache-inmemory";
|
||||||
import ApolloClient from "apollo-client";
|
import ApolloClient from "apollo-client";
|
||||||
import { BatchHttpLink } from "apollo-link-batch-http";
|
import { BatchHttpLink } from "apollo-link-batch-http";
|
||||||
import fetch from "node-fetch";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { setupPolly } from "setup-polly-jest";
|
import { setupPolly } from "setup-polly-jest";
|
||||||
|
|
||||||
|
@ -36,8 +36,7 @@ function setupApi() {
|
||||||
});
|
});
|
||||||
const cache = new InMemoryCache();
|
const cache = new InMemoryCache();
|
||||||
const link = new BatchHttpLink({
|
const link = new BatchHttpLink({
|
||||||
// @ts-ignore
|
fetch: createFetch(),
|
||||||
fetch,
|
|
||||||
uri: process.env.API_URI || "http://localhost:8000/graphql/"
|
uri: process.env.API_URI || "http://localhost:8000/graphql/"
|
||||||
});
|
});
|
||||||
const apolloClient = new ApolloClient({
|
const apolloClient = new ApolloClient({
|
||||||
|
|
Loading…
Reference in a new issue