diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index bc5886d82..8e50d2619 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1238,20 +1238,26 @@ "context": "link", "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": { "context": "description", "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": { "context": "description", "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": { "string": "New Password" }, diff --git a/package-lock.json b/package-lock.json index c78ff6f31..4791c9230 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,92 @@ "lockfileVersion": 1, "requires": true, "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": { "version": "0.20.7", "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": { "version": "2.1.4", "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": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", @@ -7414,6 +7514,21 @@ "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -20067,6 +20182,11 @@ "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": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", diff --git a/package.json b/package.json index ab8619ca1..495a90200 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "npm": ">=6.11.0 <7" }, "dependencies": { + "@apollo/client": "^3.4.15", "@editorjs/editorjs": "^2.19.3", "@editorjs/header": "^2.6.1", "@editorjs/image": "^2.6.0", @@ -28,6 +29,7 @@ "@material-ui/lab": "^4.0.0-alpha.58", "@material-ui/styles": "^4.11.4", "@saleor/macaw-ui": "^0.2.7", + "@saleor/sdk": "^0.4.0", "@sentry/react": "^6.0.0", "@types/faker": "^5.1.6", "@uiw/react-color-hue": "0.0.34", @@ -118,7 +120,6 @@ "@types/fuzzaldrin": "^2.1.2", "@types/jest": "^24.0.24", "@types/lodash-es": "^4.17.3", - "@types/node-fetch": "^2.5.7", "@types/pollyjs__adapter-node-http": "^2.0.1", "@types/pollyjs__persister-fs": "^2.0.1", "@types/react": "^16.9.16", @@ -169,7 +170,6 @@ "jest-localstorage-mock": "^2.4.3", "lint-staged": "^10.5.1", "mock-apollo-client": "^0.4.0", - "node-fetch": "^2.6.1", "prettier": "^1.19.1", "react-intl-translations-manager": "^5.0.3", "react-test-renderer": "^16.12.0", @@ -220,7 +220,7 @@ "moduleNameMapper": { "@assets(.*)$": "/assets/$1", "@locale(.*)$": "/locale/$1", - "@saleor(?!.*macaw)(.*)$": "/src/$1", + "@saleor(?!.*macaw)(?!.*sdk)(.*)$": "/src/$1", "@test/(.*)$": "/testUtils/$1", "^lodash-es(.*)$": "lodash/$1", "^@material-ui/core$": "/node_modules/@material-ui/core", diff --git a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har deleted file mode 100644 index e1740a219..000000000 --- a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har +++ /dev/null @@ -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" - } -} diff --git a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har index 5a954d4a6..691d0f2ba 100644 --- a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har +++ b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "4062f0ffdf1fab89a50cd1b667a889ef", + "_id": "2c067f705383db903cdaf88cb881c922", "_order": 0, "cache": {}, "request": { - "bodySize": 583, + "bodySize": 1154, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "583" + "value": "1154" }, { "_fromType": "array", @@ -50,40 +50,43 @@ "value": "localhost:8000" } ], - "headersSize": 281, + "headersSize": 255, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "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": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1976, + "bodySize": 679, "content": { "mimeType": "application/json", - "size": 1976, - "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\"}}}]" + "size": 679, + "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": [ { + "expires": "2021-11-10T11:17:53.000Z", "httpOnly": true, + "maxAge": 2592000, "name": "refreshToken", "path": "/", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ" + "sameSite": "Lax", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzM5NTEwNzMsIm93bmVyIjoic2FsZW9yIiwiZXhwIjoxNjM2NTQzMDczLCJ0b2tlbiI6ImdtRU95YlM5TDhmaiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJ0eXBlIjoicmVmcmVzaCIsInVzZXJfaWQiOiJWWE5sY2pveCIsImlzX3N0YWZmIjp0cnVlLCJjc3JmVG9rZW4iOiJ5RGNuM25HZ2ZXMkFYdUNjNjJwdmFMR3RMMGRLczduNTEzamhMMzBEcW1EaXJVRFhMNHp3RUpZUWZqaThMMnlQIn0.zRkkzPiC8DPlBUvUPVcOSrEF4AzgDKcK8gpyQXSpKM4" } ], "headers": [ { "name": "date", - "value": "Thu, 14 Jan 2021 13:55:44 GMT" + "value": "Mon, 11 Oct 2021 11:17:53 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.7" + "value": "WSGIServer/0.2 CPython/3.8.3" }, { "name": "content-type", @@ -91,7 +94,7 @@ }, { "name": "content-length", - "value": "1976" + "value": "679" }, { "name": "x-content-type-options", @@ -104,17 +107,17 @@ { "_fromType": "array", "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", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2021-04-22T11:07:20.652Z", - "time": 758, + "startedDateTime": "2021-10-11T11:17:52.646Z", + "time": 521, "timings": { "blocked": -1, "connect": -1, @@ -122,7 +125,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 758 + "wait": 521 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har deleted file mode 100644 index 775ee5be6..000000000 --- a/recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har +++ /dev/null @@ -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" - } -} diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har deleted file mode 100644 index a86975194..000000000 --- a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har +++ /dev/null @@ -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" - } -} diff --git a/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har b/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har deleted file mode 100644 index 6cc007de4..000000000 --- a/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har +++ /dev/null @@ -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" - } -} diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index b598d5960..032a6abb6 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "6fed086670e88883223acb33bab4ff31", + "_id": "f36c6d9d965b62862363338cf17d6135", "_order": 0, "cache": {}, "request": { - "bodySize": 599, + "bodySize": 324, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "599" + "value": "324" }, { "_fromType": "array", @@ -50,33 +50,33 @@ "value": "localhost:8000" } ], - "headersSize": 281, + "headersSize": 254, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "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": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 214, + "bodySize": 24, "content": { "mimeType": "application/json", - "size": 214, - "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"Error\"}], \"csrfToken\": null, \"token\": null, \"user\": null, \"__typename\": \"CreateToken\"}}}]" + "size": 24, + "text": "[{\"data\": {\"me\": null}}]" }, "cookies": [], "headers": [ { "name": "date", - "value": "Thu, 14 Jan 2021 13:55:45 GMT" + "value": "Thu, 07 Oct 2021 23:09:08 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.7" + "value": "WSGIServer/0.2 CPython/3.8.3" }, { "name": "content-type", @@ -84,7 +84,113 @@ }, { "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", @@ -101,8 +207,8 @@ "status": 200, "statusText": "OK" }, - "startedDateTime": "2021-04-22T11:09:20.963Z", - "time": 555, + "startedDateTime": "2021-10-11T11:17:53.202Z", + "time": 438, "timings": { "blocked": -1, "connect": -1, @@ -110,7 +216,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 555 + "wait": 438 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har index 533b49cc4..29a69e929 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-is-non-staff_2544500193/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "8948f5cbe2259e56b6dd03f068fbfa4d", + "_id": "07fe7bf33b7e219c3280229514fcf39d", "_order": 0, "cache": {}, "request": { - "bodySize": 587, + "bodySize": 1158, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "587" + "value": "1158" }, { "_fromType": "array", @@ -50,40 +50,33 @@ "value": "localhost:8000" } ], - "headersSize": 281, + "headersSize": 255, "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 {\\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": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 616, + "bodySize": 243, "content": { "mimeType": "application/json", - "size": 616, - "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\"}}}]" + "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": [ - { - "httpOnly": true, - "name": "refreshToken", - "path": "/", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMzY1ODUxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoienRDQ3EyZGpvZHpWV2VheW52OGlmUWJJSUI0bnJNNkh5SW5xY1o0eHFvYUlFZk9PS1VFaEFxdzlyVlI4Q3I4TCJ9.hDMEK3HFSLol7rLd9dfaSCLTWgvetNDAFsb039L9PXQ" - } - ], + "cookies": [], "headers": [ { "name": "date", - "value": "Tue, 19 Jan 2021 14:28:37 GMT" + "value": "Mon, 11 Oct 2021 11:17:53 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.9.1" + "value": "WSGIServer/0.2 CPython/3.8.3" }, { "name": "content-type", @@ -91,7 +84,7 @@ }, { "name": "content-length", - "value": "616" + "value": "243" }, { "name": "x-content-type-options", @@ -100,21 +93,16 @@ { "name": "referrer-policy", "value": "same-origin" - }, - { - "_fromType": "array", - "name": "set-cookie", - "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTEwNjY1MTcsImV4cCI6MTYxMzY1ODUxNywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoienRDQ3EyZGpvZHpWV2VheW52OGlmUWJJSUI0bnJNNkh5SW5xY1o0eHFvYUlFZk9PS1VFaEFxdzlyVlI4Q3I4TCJ9.hDMEK3HFSLol7rLd9dfaSCLTWgvetNDAFsb039L9PXQ; HttpOnly; Path=/" } ], - "headersSize": 619, + "headersSize": 193, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2021-04-22T11:09:21.540Z", - "time": 382, + "startedDateTime": "2021-10-11T11:17:53.664Z", + "time": 61, "timings": { "blocked": -1, "connect": -1, @@ -122,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 382 + "wait": 61 } } ], diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts deleted file mode 100644 index 13f2e0715..000000000 --- a/src/auth/AuthProvider.test.ts +++ /dev/null @@ -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) { - 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(); - }); -}); diff --git a/src/auth/AuthProvider.test.tsx b/src/auth/AuthProvider.test.tsx new file mode 100644 index 000000000..f1ce0fcc0 --- /dev/null +++ b/src/auth/AuthProvider.test.tsx @@ -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 }) => ( + {children} + ); + + 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(); + }); +}); diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 8acb73dcc..62ec9db2d 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,11 +1,10 @@ import useNotifier from "@saleor/hooks/useNotifier"; -import React, { useContext } from "react"; +import React from "react"; import { useApolloClient } from "react-apollo"; import { useIntl } from "react-intl"; import { UserContext } from "./"; import { useAuthProvider } from "./hooks/useAuthProvider"; -import { getTokens } from "./utils"; interface AuthProviderProps { children: React.ReactNode; @@ -16,24 +15,11 @@ const AuthProvider: React.FC = ({ children }) => { const intl = useIntl(); const notify = useNotifier(); - const authProvider = useAuthProvider({ apolloClient, intl, notify }); + const authProvider = useAuthProvider({ intl, notify, apolloClient }); return ( {children} ); }; -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; diff --git a/src/auth/components/LoginPage/LoginPage.stories.tsx b/src/auth/components/LoginPage/LoginPage.stories.tsx index b9151e0ad..b211927f4 100644 --- a/src/auth/components/LoginPage/LoginPage.stories.tsx +++ b/src/auth/components/LoginPage/LoginPage.stories.tsx @@ -7,7 +7,6 @@ import LoginPage, { LoginCardProps } from "../../../auth/components/LoginPage"; const props: Omit = { disabled: false, - error: false, externalAuthentications: [ { __typename: "ExternalAuthentication", @@ -15,7 +14,6 @@ const props: Omit = { name: "Example auth plugin" } ], - externalError: false, loading: false, onExternalAuthentication: () => undefined, onPasswordRecovery: undefined, @@ -26,6 +24,9 @@ storiesOf("Views / Authentication / Log in", module) .addDecorator(CardDecorator) .addDecorator(Decorator) .add("default", () => ) - .add("error", () => ) + .add("error login", () => ) + .add("error external login", () => ( + + )) .add("disabled", () => ) .add("loading", () => ); diff --git a/src/auth/components/LoginPage/LoginPage.tsx b/src/auth/components/LoginPage/LoginPage.tsx index a8bf81b3e..85979080c 100644 --- a/src/auth/components/LoginPage/LoginPage.tsx +++ b/src/auth/components/LoginPage/LoginPage.tsx @@ -5,6 +5,7 @@ import { TextField, Typography } from "@material-ui/core"; +import { UserContextError } from "@saleor/auth/types"; import { AvailableExternalAuthentications_shop_availableExternalAuthentications } from "@saleor/auth/types/AvailableExternalAuthentications"; import { FormSpacer } from "@saleor/components/FormSpacer"; import { SubmitPromise } from "@saleor/hooks/useForm"; @@ -14,6 +15,7 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import LoginForm, { LoginFormData } from "./form"; +import { getErrorMessage } from "./messages"; const useStyles = makeStyles( theme => ({ @@ -49,8 +51,7 @@ const useStyles = makeStyles( ); export interface LoginCardProps { - error: boolean; - externalError: boolean; + error?: UserContextError; disabled: boolean; loading: boolean; externalAuthentications?: AvailableExternalAuthentications_shop_availableExternalAuthentications[]; @@ -62,7 +63,6 @@ export interface LoginCardProps { const LoginCard: React.FC = props => { const { error, - externalError, disabled, loading, externalAuthentications = [], @@ -84,19 +84,12 @@ const LoginCard: React.FC = props => { return ( - {({ change: handleChange, data, submit: handleSubmit }) => ( + {({ change: handleChange, data }) => ( <> {error && (
- - -
- )} - {externalError && ( -
- - + {getErrorMessage(error, intl)}
)} @@ -136,7 +129,6 @@ const LoginCard: React.FC = props => { color="primary" disabled={disabled} variant="contained" - onClick={handleSubmit} type="submit" data-test="submit" > @@ -184,7 +176,6 @@ const LoginCard: React.FC = props => { color="primary" fullWidth variant="outlined" - size="large" onClick={() => onExternalAuthentication(externalAuthentication.id) } diff --git a/src/auth/components/LoginPage/form.tsx b/src/auth/components/LoginPage/form.tsx index 8dbd7389d..4da636f77 100644 --- a/src/auth/components/LoginPage/form.tsx +++ b/src/auth/components/LoginPage/form.tsx @@ -12,7 +12,7 @@ export interface UseLoginFormResult { change: FormChange; data: LoginFormData; hasChanged: boolean; - submit: () => Promise; + submit: (event: React.FormEvent) => Promise; } export interface LoginFormProps { @@ -53,7 +53,11 @@ function useLoginForm( return errors; }; - const submit = async () => handleFormSubmit(data, handleSubmit, setChanged); + const submit = async (event: React.FormEvent) => { + event.preventDefault(); + + return handleFormSubmit(data, handleSubmit, setChanged); + }; return { change: handleChange, diff --git a/src/auth/components/LoginPage/messages.ts b/src/auth/components/LoginPage/messages.ts new file mode 100644 index 000000000..f4cabf77a --- /dev/null +++ b/src/auth/components/LoginPage/messages.ts @@ -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); + } +} diff --git a/src/auth/components/NewPasswordPage/NewPasswordPage.stories.tsx b/src/auth/components/NewPasswordPage/NewPasswordPage.stories.tsx index 83fec54f5..a6c6e6d4c 100644 --- a/src/auth/components/NewPasswordPage/NewPasswordPage.stories.tsx +++ b/src/auth/components/NewPasswordPage/NewPasswordPage.stories.tsx @@ -21,7 +21,8 @@ storiesOf("Views / Authentication / Set up a new password", module) __typename: "AccountError", code: AccountErrorCode.PASSWORD_TOO_SHORT, field, - addressType: null + addressType: null, + message: null }))} disabled={false} onSubmit={() => undefined} diff --git a/src/auth/components/NewPasswordPage/NewPasswordPage.tsx b/src/auth/components/NewPasswordPage/NewPasswordPage.tsx index d2470e794..86e63524d 100644 --- a/src/auth/components/NewPasswordPage/NewPasswordPage.tsx +++ b/src/auth/components/NewPasswordPage/NewPasswordPage.tsx @@ -1,8 +1,8 @@ import { Button, TextField, Typography } from "@material-ui/core"; -import { SetPassword_setPassword_errors } from "@saleor/auth/types/SetPassword"; import Form from "@saleor/components/Form"; import FormSpacer from "@saleor/components/FormSpacer"; import { makeStyles } from "@saleor/macaw-ui"; +import { SetPasswordData } from "@saleor/sdk"; import getAccountErrorMessage from "@saleor/utils/errors/account"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -33,7 +33,7 @@ export interface NewPasswordPageFormData { } export interface NewPasswordPageProps { disabled: boolean; - errors: SetPassword_setPassword_errors[]; + errors: SetPasswordData["errors"]; onSubmit: (data: NewPasswordPageFormData) => void; } diff --git a/src/auth/components/SectionRoute.tsx b/src/auth/components/SectionRoute.tsx index f7e93c31a..91428fe0f 100644 --- a/src/auth/components/SectionRoute.tsx +++ b/src/auth/components/SectionRoute.tsx @@ -1,7 +1,7 @@ -import useUser from "@saleor/hooks/useUser"; import React from "react"; import { Route, RouteProps } from "react-router-dom"; +import { useUser } from ".."; import NotFound from "../../NotFound"; import { PermissionEnum } from "../../types/globalTypes"; import { hasAllPermissions, hasAnyPermissions } from "../misc"; diff --git a/src/auth/hooks/useAuthProvider.ts b/src/auth/hooks/useAuthProvider.ts index ea3260469..48c0b8d02 100644 --- a/src/auth/hooks/useAuthProvider.ts +++ b/src/auth/hooks/useAuthProvider.ts @@ -1,58 +1,181 @@ import { IMessageContext } from "@saleor/components/messages"; -import { User } from "@saleor/fragments/types/User"; -import useLocalStorage from "@saleor/hooks/useLocalStorage"; +import { APP_DEFAULT_URI, APP_MOUNT_URI, DEMO_MODE } from "@saleor/config"; +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 { MutableRefObject } from "react"; +import { useEffect, useState } from "react"; +import { useQuery } from "react-apollo"; import { IntlShape } from "react-intl"; +import urlJoin from "url-join"; -import { useExternalAuthProvider } from "./useExternalAuthProvider"; -import { useSaleorAuthProvider } from "./useSaleorAuthProvider"; +import { userDetailsQuery } from "../queries"; +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; - tokenVerifyLoading: boolean; - user?: User; - autologinPromise?: MutableRefObject>; -} export interface UseAuthProviderOpts { intl: IntlShape; notify: IMessageContext; apolloClient: ApolloClient; } -export function useAuthProvider(opts: UseAuthProviderOpts) { - const [authPlugin, setAuthPlugin] = useLocalStorage("authPlugin", undefined); +export function useAuthProvider({ + intl, + notify, + apolloClient +}: UseAuthProviderOpts): UserContext { + const { + login, + getExternalAuthUrl, + getExternalAccessToken, + logout + } = useAuth(); + const { authenticated, authenticating, user } = useAuthState(); + const [error, setError] = useState(); - const saleorAuth = useSaleorAuthProvider({ - authPlugin, - setAuthPlugin, - ...opts + useEffect(() => { + if (authenticating && error) { + setError(undefined); + } + }, [authenticating]); + + useEffect(() => { + if (!authenticated && !authenticating) { + loginWithCredentialsManagementAPI(handleLogin); + } + }, [authenticated, authenticating]); + + const userDetails = useQuery(userDetailsQuery, { + client: apolloClient, + skip: !authenticated }); - const externalAuth = useExternalAuthProvider({ - authPlugin, - setAuthPlugin, - ...opts - }); + const handleLogout = async () => { + const result = await logout({ + input: JSON.stringify({ + returnTo: urlJoin( + window.location.origin, + APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI + ) + } as RequestExternalLogoutInput) + }); - const loginAuth = { - login: saleorAuth.login, - loginByExternalPlugin: externalAuth.loginByExternalPlugin, - loginByToken: saleorAuth.loginByToken, - requestLoginByExternalPlugin: externalAuth.requestLoginByExternalPlugin + if (isCredentialsManagementAPISupported) { + navigator.credentials.preventSilentAccess(); + } + + 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) { - return { - ...externalAuth, - ...loginAuth - }; - } + const handleLogin = async (email: string, password: string) => { + try { + const result = await login({ + 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 { - ...saleorAuth, - ...loginAuth + login: handleLogin, + requestLoginByExternalPlugin: handleRequestExternalLogin, + loginByExternalPlugin: handleExternalLogin, + logout: handleLogout, + authenticating: authenticating && !error, + authenticated: authenticated && user?.isStaff, + user: userDetails.data?.me, + error }; } diff --git a/src/auth/hooks/useExternalAuthProvider.ts b/src/auth/hooks/useExternalAuthProvider.ts deleted file mode 100644 index d15925e79..000000000 --- a/src/auth/hooks/useExternalAuthProvider.ts +++ /dev/null @@ -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; - loginByExternalPlugin: ( - input: ExternalLoginInput - ) => Promise; -} -export interface UseExternalAuthProviderOpts extends UseAuthProviderOpts { - setAuthPlugin: Dispatch>; - authPlugin: string; -} - -const persistToken = false; - -export function useExternalAuthProvider({ - apolloClient, - authPlugin, - intl, - notify, - setAuthPlugin -}: UseExternalAuthProviderOpts): UseExternalAuthProvider { - const [userContext, setUserContext] = useState(undefined); - const autologinPromise = useRef>(); - const refreshPromise = useRef>(); - - 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 => { - 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 - }; -} diff --git a/src/auth/hooks/useSaleorAuthProvider.ts b/src/auth/hooks/useSaleorAuthProvider.ts deleted file mode 100644 index 0f67fc05b..000000000 --- a/src/auth/hooks/useSaleorAuthProvider.ts +++ /dev/null @@ -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; - loginByToken: (auth: string, csrf: string, user: User) => void; -} -export interface UseSaleorAuthProviderOpts extends UseAuthProviderOpts { - setAuthPlugin: Dispatch>; - authPlugin: string; -} - -const persistToken = false; - -export function useSaleorAuthProvider({ - apolloClient, - authPlugin, - intl, - notify, - setAuthPlugin -}: UseSaleorAuthProviderOpts): UseSaleorAuthProvider { - const [userContext, setUserContext] = useState(undefined); - const autologinPromise = useRef>(); - const refreshPromise = useRef>(); - - 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( - 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 => { - 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 - }; -} diff --git a/src/auth/index.tsx b/src/auth/index.tsx index b46640c16..1238b1a65 100644 --- a/src/auth/index.tsx +++ b/src/auth/index.tsx @@ -1,15 +1,9 @@ -import { User } from "@saleor/fragments/types/User"; 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 Layout from "./components/Layout"; -import { - ExternalLoginInput, - RequestExternalLoginInput -} from "./hooks/useExternalAuthProvider"; -import { ExternalObtainAccessTokens_externalObtainAccessTokens } from "./types/ExternalObtainAccessTokens"; -import { TokenAuth_tokenCreate } from "./types/TokenAuth"; +import { UserContext as Context } from "./types"; import { LoginUrlQueryParams, newPasswordPath, @@ -28,33 +22,13 @@ const LoginView: React.FC> = () => { return ; }; -interface UserContext { - login: (username: string, password: string) => Promise; - loginByExternalPlugin: ( - input: ExternalLoginInput - ) => Promise; - loginByToken: (auth: string, csrf: string, user: User) => void; - logout: () => void; - requestLoginByExternalPlugin: ( - pluginId: string, - input: RequestExternalLoginInput - ) => Promise; - tokenAuthLoading: boolean; - tokenRefresh: () => Promise; - tokenVerifyLoading: boolean; - user?: User; - autologinPromise?: MutableRefObject>; -} - -export const UserContext = React.createContext({ +export const UserContext = React.createContext({ login: undefined, loginByExternalPlugin: undefined, - loginByToken: undefined, logout: undefined, requestLoginByExternalPlugin: undefined, - tokenAuthLoading: false, - tokenRefresh: undefined, - tokenVerifyLoading: false + authenticating: false, + authenticated: false }); const AuthRouter: React.FC = () => ( @@ -72,3 +46,4 @@ AuthRouter.displayName = "AuthRouter"; export default AuthRouter; export * from "./utils"; +export const useUser = () => useContext(UserContext); diff --git a/src/auth/link.ts b/src/auth/link.ts deleted file mode 100644 index fefe8b24f..000000000 --- a/src/auth/link.ts +++ /dev/null @@ -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; diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 30a65917e..abc453645 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -1,4 +1,3 @@ -import { fragmentUser } from "@saleor/fragments/auth"; import { accountErrorFragment } from "@saleor/fragments/errors"; import gql from "graphql-tag"; @@ -7,44 +6,6 @@ import { RequestPasswordReset, RequestPasswordResetVariables } 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` ${accountErrorFragment} @@ -60,74 +21,3 @@ export const RequestPasswordResetMutation = TypedMutation< RequestPasswordReset, RequestPasswordResetVariables >(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 - } - } - } -`; diff --git a/src/auth/queries.ts b/src/auth/queries.ts index 18304273e..291db516f 100644 --- a/src/auth/queries.ts +++ b/src/auth/queries.ts @@ -1,3 +1,4 @@ +import { fragmentUser } from "@saleor/fragments/auth"; import gql from "graphql-tag"; export const availableExternalAuthentications = gql` @@ -10,3 +11,12 @@ export const availableExternalAuthentications = gql` } } `; + +export const userDetailsQuery = gql` + ${fragmentUser} + query UserDetails { + me { + ...User + } + } +`; diff --git a/src/auth/types.ts b/src/auth/types.ts new file mode 100644 index 000000000..74c107005 --- /dev/null +++ b/src/auth/types.ts @@ -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; + loginByExternalPlugin: ( + pluginId: string, + input: ExternalLoginInput + ) => Promise; + logout: () => Promise; + requestLoginByExternalPlugin: ( + pluginId: string, + input: RequestExternalLoginInput + ) => Promise; + user?: User; + authenticating: boolean; + authenticated: boolean; + error?: UserContextError; +} diff --git a/src/auth/types/UserDetails.ts b/src/auth/types/UserDetails.ts new file mode 100644 index 000000000..9ac5b520f --- /dev/null +++ b/src/auth/types/UserDetails.ts @@ -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; +} diff --git a/src/auth/utils.ts b/src/auth/utils.ts index 4ea5c4afb..3ed4b86a2 100644 --- a/src/auth/utils.ts +++ b/src/auth/utils.ts @@ -6,43 +6,6 @@ import { IntlShape } from "react-intl"; 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 = ( intl: IntlShape, notify: UseNotifierResult @@ -55,23 +18,17 @@ export const displayDemoMessage = ( export async function handleQueryAuthError( error: ApolloError, notify: IMessageContext, - tokenRefresh: () => Promise, logout: () => void, intl: IntlShape ) { if (error.graphQLErrors.some(isJwtError)) { + logout(); if (error.graphQLErrors.every(isTokenExpired)) { - const success = await tokenRefresh(); - - if (!success) { - logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.sessionExpired) - }); - } + notify({ + status: "error", + text: intl.formatMessage(commonMessages.sessionExpired) + }); } else { - logout(); notify({ status: "error", text: intl.formatMessage(commonMessages.somethingWentWrong) diff --git a/src/auth/views/Login.tsx b/src/auth/views/Login.tsx index 59f7b1878..30fe46fd7 100644 --- a/src/auth/views/Login.tsx +++ b/src/auth/views/Login.tsx @@ -1,11 +1,12 @@ import { APP_DEFAULT_URI, APP_MOUNT_URI } from "@saleor/config"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; -import useUser from "@saleor/hooks/useUser"; -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useQuery } from "react-apollo"; import urlJoin from "url-join"; import useRouter from "use-react-router"; +import { useUser } from ".."; import LoginPage from "../components/LoginPage"; import { LoginFormData } from "../components/LoginPage/form"; import { availableExternalAuthentications } from "../queries"; @@ -27,46 +28,50 @@ const LoginView: React.FC = ({ params }) => { login, requestLoginByExternalPlugin, loginByExternalPlugin, - tokenAuthLoading + authenticating, + error } = useUser(); - const [isError, setIsError] = useState(false); - const [isExternalError, setIsExternalError] = useState(false); const { data: externalAuthentications, loading: externalAuthenticationsLoading } = useQuery( availableExternalAuthentications ); + const [ + requestedExternalPluginId, + setRequestedExternalPluginId + ] = useLocalStorage("requestedExternalPluginId", null); const handleSubmit = async (data: LoginFormData) => { const result = await login(data.email, data.password); const errors = result?.errors || []; - setIsExternalError(false); - setIsError(!result || errors?.length > 0); return errors; }; - const handleRequestExternalAuthentication = (pluginId: string) => - requestLoginByExternalPlugin(pluginId, { + const handleRequestExternalAuthentication = async (pluginId: string) => { + const result = await requestLoginByExternalPlugin(pluginId, { redirectUri: urlJoin( window.location.origin, APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI, 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 result = await loginByExternalPlugin({ code, state }); - const errors = result?.errors || []; - - setIsError(false); - if (!result || errors?.length > 0) { - setIsExternalError(true); - } else { + const result = await loginByExternalPlugin(requestedExternalPluginId, { + code, + state + }); + if (result && !result?.errors?.length) { navigate(APP_DEFAULT_URI); } - return errors; }; useEffect(() => { @@ -80,13 +85,12 @@ const LoginView: React.FC = ({ params }) => { return ( navigate(passwordResetUrl)} onSubmit={handleSubmit} diff --git a/src/auth/views/NewPassword.tsx b/src/auth/views/NewPassword.tsx index 7eefe2fac..cae172bee 100644 --- a/src/auth/views/NewPassword.tsx +++ b/src/auth/views/NewPassword.tsx @@ -1,54 +1,49 @@ 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 React from "react"; +import React, { useState } from "react"; import { RouteComponentProps } from "react-router"; import NewPasswordPage, { NewPasswordPageFormData } from "../components/NewPasswordPage"; -import { SetPasswordMutation } from "../mutations"; -import { SetPassword } from "../types/SetPassword"; import { NewPasswordUrlQueryParams } from "../urls"; const NewPassword: React.FC = ({ location }) => { const navigate = useNavigator(); - const { loginByToken } = useUser(); + + const { setPassword } = useAuth(); + + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([]); const params: NewPasswordUrlQueryParams = parseQs(location.search.substr(1)); - const handleSetPassword = async (data: SetPassword) => { - if (data.setPassword.errors.length === 0) { - loginByToken( - data.setPassword.token, - data.setPassword.csrfToken, - data.setPassword.user - ); + const handleSubmit = async (data: NewPasswordPageFormData) => { + setLoading(true); + + const result = await setPassword({ + email: params.email, + password: data.password, + token: params.token + }); + + const errors = result.data?.setPassword?.errors || []; + + setErrors(errors); + setLoading(false); + + if (!errors.length) { navigate("/", { replace: true }); } }; return ( - - {(setPassword, setPasswordOpts) => { - const handleSubmit = (data: NewPasswordPageFormData) => - setPassword({ - variables: { - email: params.email, - password: data.password, - token: params.token - } - }); - - return ( - - ); - }} - + ); }; diff --git a/src/components/AccountPermissions/AccountPermissions.tsx b/src/components/AccountPermissions/AccountPermissions.tsx index b646ce4b7..75978fc21 100644 --- a/src/components/AccountPermissions/AccountPermissions.tsx +++ b/src/components/AccountPermissions/AccountPermissions.tsx @@ -8,9 +8,9 @@ import { ListItemText, Typography } from "@material-ui/core"; +import { useUser } from "@saleor/auth"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; -import useUser from "@saleor/hooks/useUser"; import { makeStyles } from "@saleor/macaw-ui"; import { PermissionData } from "@saleor/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage"; import React from "react"; diff --git a/src/components/AppLayout/AppChannelContext.tsx b/src/components/AppLayout/AppChannelContext.tsx index 94f5bc0e3..bccef5e1d 100644 --- a/src/components/AppLayout/AppChannelContext.tsx +++ b/src/components/AppLayout/AppChannelContext.tsx @@ -1,9 +1,10 @@ -import { useAuth } from "@saleor/auth/AuthProvider"; +import { useUser } from "@saleor/auth"; import { useBaseChannelsList } from "@saleor/channels/queries"; import { BaseChannels_channels } from "@saleor/channels/types/BaseChannels"; import { ChannelFragment } from "@saleor/fragments/types/ChannelFragment"; import useLocalStorage from "@saleor/hooks/useLocalStorage"; import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; +import { useSaleorConfig } from "@saleor/sdk"; import React from "react"; interface UseAppChannel { @@ -38,10 +39,11 @@ const isValidChannel = ( }; export const AppChannelProvider: React.FC = ({ children }) => { - const { isAuthenticated } = useAuth(); + const { setChannel } = useSaleorConfig(); + const { authenticated } = useUser(); const [selectedChannel, setSelectedChannel] = useLocalStorage("channel", ""); const { data: channelData, refetch } = useBaseChannelsList({ - skip: !isAuthenticated + skip: !authenticated }); const [isPickerActive, setPickerActive] = React.useState(false); @@ -54,6 +56,10 @@ export const AppChannelProvider: React.FC = ({ children }) => { } }, [channelData]); + React.useEffect(() => { + setChannel(selectedChannel); + }, [selectedChannel]); + const availableChannels = channelData?.channels || []; const channel = diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index 65fde31a4..a73f65661 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -1,7 +1,7 @@ import { LinearProgress, useMediaQuery } from "@material-ui/core"; +import { useUser } from "@saleor/auth"; import useAppState from "@saleor/hooks/useAppState"; import useNavigator from "@saleor/hooks/useNavigator"; -import useUser from "@saleor/hooks/useUser"; import { makeStyles, SaleorTheme, diff --git a/src/components/ChannelsAvailabilityCard/ChannelsAvailabilityCardWrapper.tsx b/src/components/ChannelsAvailabilityCard/ChannelsAvailabilityCardWrapper.tsx index 6c89023c7..63737502b 100644 --- a/src/components/ChannelsAvailabilityCard/ChannelsAvailabilityCardWrapper.tsx +++ b/src/components/ChannelsAvailabilityCard/ChannelsAvailabilityCardWrapper.tsx @@ -1,8 +1,8 @@ import { Button, Card, CardContent, Typography } from "@material-ui/core"; +import { useUser } from "@saleor/auth"; import CardTitle from "@saleor/components/CardTitle"; import Hr from "@saleor/components/Hr"; import RequirePermissions from "@saleor/components/RequirePermissions"; -import useUser from "@saleor/hooks/useUser"; import { PermissionEnum } from "@saleor/types/globalTypes"; import React from "react"; import { useIntl } from "react-intl"; diff --git a/src/components/Shop/index.tsx b/src/components/Shop/index.tsx index 035598cd0..51e2cf321 100644 --- a/src/components/Shop/index.tsx +++ b/src/components/Shop/index.tsx @@ -2,7 +2,7 @@ import appleTouchIcon from "@assets/favicons/apple-touch-icon.png"; import favicon16 from "@assets/favicons/favicon-16x16.png"; import favicon32 from "@assets/favicons/favicon-32x32.png"; 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 Helmet from "react-helmet"; @@ -14,10 +14,10 @@ type ShopContext = ShopInfo_shop; export const ShopContext = React.createContext(undefined); export const ShopProvider: React.FC = ({ children }) => { - const { isAuthenticated } = useAuth(); + const { authenticated } = useUser(); return ( - + {({ data }) => ( <> diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 603034ab3..36f40719c 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -1,10 +1,10 @@ import { attributeListUrl } from "@saleor/attributes/urls"; +import { useUser } from "@saleor/auth"; import { channelsListUrl } from "@saleor/channels/urls"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { APP_VERSION as dashboardVersion } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; import useShop from "@saleor/hooks/useShop"; -import useUser from "@saleor/hooks/useUser"; import Attributes from "@saleor/icons/Attributes"; import Channels from "@saleor/icons/Channels"; import Navigation from "@saleor/icons/Navigation"; diff --git a/src/home/views/index.tsx b/src/home/views/index.tsx index c264e436c..a93c04e09 100644 --- a/src/home/views/index.tsx +++ b/src/home/views/index.tsx @@ -1,7 +1,7 @@ +import { useUser } from "@saleor/auth"; import { channelsListUrl } from "@saleor/channels/urls"; import useAppChannel from "@saleor/components/AppLayout/AppChannelContext"; import useNavigator from "@saleor/hooks/useNavigator"; -import useUser from "@saleor/hooks/useUser"; import { mapEdgesToItems } from "@saleor/utils/maps"; import React from "react"; diff --git a/src/hooks/makeMutation.ts b/src/hooks/makeMutation.ts index e809e1a59..ecd509646 100644 --- a/src/hooks/makeMutation.ts +++ b/src/hooks/makeMutation.ts @@ -1,3 +1,4 @@ +import { useUser } from "@saleor/auth"; import { isJwtError } from "@saleor/auth/errors"; import { commonMessages } from "@saleor/intl"; import { getMutationStatus } from "@saleor/misc"; @@ -13,7 +14,6 @@ import { import { useIntl } from "react-intl"; import useNotifier from "./useNotifier"; -import useUser from "./useUser"; export type MutationResultWithOpts = MutationResult & MutationResultAdditionalProps; diff --git a/src/hooks/makeQuery.ts b/src/hooks/makeQuery.ts index 8f5938e8f..b37fa276f 100644 --- a/src/hooks/makeQuery.ts +++ b/src/hooks/makeQuery.ts @@ -1,4 +1,5 @@ import { handleQueryAuthError } from "@saleor/auth"; +import { useUser } from "@saleor/auth"; import { RequireAtLeastOne } from "@saleor/misc"; import { ApolloQueryResult, WatchQueryFetchPolicy } from "apollo-client"; import { DocumentNode } from "graphql"; @@ -11,7 +12,6 @@ import { PrefixedPermissions } from "../types/extendedTypes"; import { PermissionEnum } from "../types/globalTypes"; import useAppState from "./useAppState"; import useNotifier from "./useNotifier"; -import useUser from "./useUser"; const getPermissionKey = (permission: string) => `PERMISSION_${permission}` as PrefixedPermissions; @@ -81,14 +81,7 @@ function makeQuery( }, errorPolicy: "all", fetchPolicy: fetchPolicy || "cache-and-network", - onError: error => - handleQueryAuthError( - error, - notify, - user.tokenRefresh, - user.logout, - intl - ), + onError: error => handleQueryAuthError(error, notify, user.logout, intl), skip, variables: variablesWithPermissions }); diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts deleted file mode 100644 index 13e6c3fd0..000000000 --- a/src/hooks/useUser.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useContext } from "react"; - -import { UserContext } from "../auth"; - -function useUser() { - const user = useContext(UserContext); - return user; -} -export default useUser; diff --git a/src/index.tsx b/src/index.tsx index ee4168d56..c0e43bd9f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import DemoBanner from "@saleor/components/DemoBanner"; import useAppState from "@saleor/hooks/useAppState"; import { ThemeProvider } from "@saleor/macaw-ui"; +import { createFetch, createSaleorClient, SaleorProvider } from "@saleor/sdk"; import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory"; import { IntrospectionFragmentMatcher } from "apollo-cache-inmemory"; import { ApolloClient } from "apollo-client"; @@ -21,11 +22,10 @@ import { ExternalAppProvider } from "./apps/components/ExternalAppContext"; import { appsSection } from "./apps/urls"; import AttributeSection from "./attributes"; import { attributeSection } from "./attributes/urls"; -import Auth from "./auth"; -import AuthProvider, { useAuth } from "./auth/AuthProvider"; +import Auth, { useUser } from "./auth"; +import AuthProvider from "./auth/AuthProvider"; import LoginLoading from "./auth/components/LoginLoading/LoginLoading"; import SectionRoute from "./auth/components/SectionRoute"; -import authLink from "./auth/link"; import CategorySection from "./categories"; import ChannelsSection from "./channels"; import { channelsSection } from "./channels/urls"; @@ -83,7 +83,8 @@ errorTracker.init(); // so we need to explicitly set them const linkOptions = { credentials: "include", - uri: API_URI + uri: API_URI, + fetch: createFetch() }; const uploadLink = createUploadLink(linkOptions); const batchLink = new BatchHttpLink({ @@ -113,60 +114,57 @@ const apolloClient = new ApolloClient({ return defaultDataIdFromObject(obj); } }), - link: authLink.concat(link) + link +}); + +const saleorClient = createSaleorClient({ + apiUrl: API_URI, + channel: "" }); const App: React.FC = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); const Routes: React.FC = () => { const intl = useIntl(); const [, dispatchAppState] = useAppState(); - const { - hasToken, - isAuthenticated, - tokenAuthLoading, - tokenVerifyLoading - } = useAuth(); + const { authenticated, authenticating } = useUser(); const { channel } = useAppChannel(false); const channelLoaded = typeof channel !== "undefined"; - const homePageLoaded = - channelLoaded && - isAuthenticated && - !tokenAuthLoading && - !tokenVerifyLoading; + const homePageLoaded = channelLoaded && authenticated; - const homePageLoading = - (isAuthenticated && !channelLoaded) || (hasToken && tokenVerifyLoading); + const homePageLoading = (authenticated && !channelLoaded) || authenticating; return ( <> diff --git a/src/mutations.tsx b/src/mutations.tsx index 5b68d9064..e41241e86 100644 --- a/src/mutations.tsx +++ b/src/mutations.tsx @@ -4,9 +4,9 @@ import React from "react"; import { Mutation, MutationFunction, MutationResult } from "react-apollo"; import { useIntl } from "react-intl"; +import { useUser } from "./auth"; import { isJwtError } from "./auth/errors"; import useNotifier from "./hooks/useNotifier"; -import useUser from "./hooks/useUser"; import { commonMessages } from "./intl"; import { getMutationStatus } from "./misc"; import { MutationResultAdditionalProps } from "./types"; diff --git a/src/orders/views/OrderDetails/OrderDraftDetails/index.tsx b/src/orders/views/OrderDetails/OrderDraftDetails/index.tsx index db3459f80..65e929a5c 100644 --- a/src/orders/views/OrderDetails/OrderDraftDetails/index.tsx +++ b/src/orders/views/OrderDetails/OrderDraftDetails/index.tsx @@ -1,8 +1,8 @@ +import { useUser } from "@saleor/auth"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import { useCustomerAddressesQuery } from "@saleor/customers/queries"; import useNavigator from "@saleor/hooks/useNavigator"; -import useUser from "@saleor/hooks/useUser"; import { CustomerEditData } from "@saleor/orders/components/OrderCustomer"; import OrderCustomerAddressesEditDialog, { OrderCustomerAddressesEditDialogOutput diff --git a/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx b/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx index 3d1b9bf38..f915d3f40 100644 --- a/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx +++ b/src/orders/views/OrderDetails/OrderNormalDetails/index.tsx @@ -1,6 +1,6 @@ +import { useUser } from "@saleor/auth"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; -import useUser from "@saleor/hooks/useUser"; import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog"; import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog"; diff --git a/src/orders/views/OrderDetails/OrderUnconfirmedDetails/index.tsx b/src/orders/views/OrderDetails/OrderUnconfirmedDetails/index.tsx index 85ebb928b..83e6eca04 100644 --- a/src/orders/views/OrderDetails/OrderUnconfirmedDetails/index.tsx +++ b/src/orders/views/OrderDetails/OrderUnconfirmedDetails/index.tsx @@ -1,7 +1,7 @@ +import { useUser } from "@saleor/auth"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; -import useUser from "@saleor/hooks/useUser"; import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog"; import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog"; diff --git a/src/permissionGroups/views/PermissionGroupCreate/PermissionGroupCreate.tsx b/src/permissionGroups/views/PermissionGroupCreate/PermissionGroupCreate.tsx index 5e18ed71e..c931c412c 100644 --- a/src/permissionGroups/views/PermissionGroupCreate/PermissionGroupCreate.tsx +++ b/src/permissionGroups/views/PermissionGroupCreate/PermissionGroupCreate.tsx @@ -1,8 +1,8 @@ +import { useUser } from "@saleor/auth"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; -import useUser from "@saleor/hooks/useUser"; import { PermissionData } from "@saleor/permissionGroups/components/PermissionGroupDetailsPage"; import React from "react"; import { useIntl } from "react-intl"; diff --git a/src/permissionGroups/views/PermissionGroupDetails/PermissionGroupDetails.tsx b/src/permissionGroups/views/PermissionGroupDetails/PermissionGroupDetails.tsx index 9d4c53a91..b41922382 100644 --- a/src/permissionGroups/views/PermissionGroupDetails/PermissionGroupDetails.tsx +++ b/src/permissionGroups/views/PermissionGroupDetails/PermissionGroupDetails.tsx @@ -1,11 +1,11 @@ import { Button } from "@material-ui/core"; +import { useUser } from "@saleor/auth"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; -import useUser from "@saleor/hooks/useUser"; import { commonMessages } from "@saleor/intl"; import MembersErrorDialog from "@saleor/permissionGroups/components/MembersErrorDialog"; import { diff --git a/src/queries.tsx b/src/queries.tsx index 3f7e039cb..d72bb0d3f 100644 --- a/src/queries.tsx +++ b/src/queries.tsx @@ -4,10 +4,9 @@ import React from "react"; import { Query, QueryResult } from "react-apollo"; import { useIntl } from "react-intl"; -import { handleQueryAuthError } from "./auth"; +import { handleQueryAuthError, useUser } from "./auth"; import useAppState from "./hooks/useAppState"; import useNotifier from "./hooks/useNotifier"; -import useUser from "./hooks/useUser"; import { RequireAtLeastOne } from "./misc"; export interface LoadMore { @@ -79,13 +78,7 @@ export function TypedQuery( context={{ useBatching: true }} errorPolicy="all" onError={error => - handleQueryAuthError( - error, - notify, - user.tokenRefresh, - user.logout, - intl - ) + handleQueryAuthError(error, notify, user.logout, intl) } > {(queryData: QueryResult) => { diff --git a/src/shipping/views/ShippingZonesList.tsx b/src/shipping/views/ShippingZonesList.tsx index d3640db68..5ea59ec6d 100644 --- a/src/shipping/views/ShippingZonesList.tsx +++ b/src/shipping/views/ShippingZonesList.tsx @@ -1,5 +1,6 @@ import { DialogContentText, IconButton } from "@material-ui/core"; import DeleteIcon from "@material-ui/icons/Delete"; +import { useUser } from "@saleor/auth"; import ActionDialog from "@saleor/components/ActionDialog"; import { configurationMenuUrl } from "@saleor/configuration"; import useBulkActions from "@saleor/hooks/useBulkActions"; @@ -11,7 +12,6 @@ import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; -import useUser from "@saleor/hooks/useUser"; import { commonMessages } from "@saleor/intl"; import { getStringOrPlaceholder, maybe } from "@saleor/misc"; import { getById } from "@saleor/orders/components/OrderReturnPage/utils"; diff --git a/src/staff/views/StaffDetails.tsx b/src/staff/views/StaffDetails.tsx index 7413caa9f..293f47749 100644 --- a/src/staff/views/StaffDetails.tsx +++ b/src/staff/views/StaffDetails.tsx @@ -1,11 +1,11 @@ import { DialogContentText } from "@material-ui/core"; +import { useUser } from "@saleor/auth"; import ActionDialog from "@saleor/components/ActionDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; -import useUser from "@saleor/hooks/useUser"; import { commonMessages, errorMessages } from "@saleor/intl"; import { getStringOrPlaceholder, maybe } from "@saleor/misc"; import usePermissionGroupSearch from "@saleor/searches/usePermissionGroupSearch"; diff --git a/src/storybook/UserDecorator.tsx b/src/storybook/UserDecorator.tsx index a420d1b10..dffad43bc 100644 --- a/src/storybook/UserDecorator.tsx +++ b/src/storybook/UserDecorator.tsx @@ -6,14 +6,12 @@ export const UserDecorator = (user: User) => storyFn => ( {storyFn()} diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 0eca7dbcf..3063cc97a 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -36087,7 +36087,7 @@ exports[`Storyshots Views / Authentication / Log in default 1`] = ` class="FormSpacer-spacer-id" /> + +
+
+ Forgot password? + + Use this link to recover it + +
+
+
+
+
+ or login using +
+
+ + +
+
+
+`; + +exports[`Storyshots Views / Authentication / Log in error login 1`] = `
@@ -36409,7 +36573,7 @@ exports[`Storyshots Views / Authentication / Log in error 1`] = ` class="FormSpacer-spacer-id" />