From ac38eb31ecf58852b938940f75b9b8ec9b32adcd Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Sun, 27 Aug 2023 23:48:56 +0200 Subject: [PATCH] fix: handle null in provider list of the tenant config --- CHANGELOG.md | 23 ++- .../for-tests-react-16/src/testContext.js | 2 +- examples/for-tests/src/testContext.js | 2 +- lib/build/thirdparty-shared.js | 12 +- lib/build/thirdparty-shared2.js | 4 +- lib/build/version.d.ts | 2 +- .../components/features/signInAndUp/index.tsx | 4 +- lib/ts/recipe/thirdparty/utils.ts | 3 +- lib/ts/version.ts | 2 +- package-lock.json | 18 +-- package.json | 4 +- ...multitenancy.dynamic_login_methods.test.js | 133 +++++++++++++++--- 12 files changed, 164 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3cb4c555..581097a47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.34.2] - 2023-08-27 + +### Fixes + +- Fixed the SDK trying to merge the providers from the tenant config if the third party login method is disabled. + ## [0.34.1] - 2023-07-31 ### Changes @@ -35,10 +41,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Backend SDKs have to be updated first to a version that supports multi-tenancy for thirdparty - supertokens-node: >= 15.0.0 - In ThirdParty recipe, + - Changed signatures of the functions `getAuthorisationURLWithQueryParamsAndSetState` + ```diff import { getAuthorisationURLWithQueryParamsAndSetState } from 'supertokens-auth-react/recipe/thirdparty'; - + getAuthorisationURLWithQueryParamsAndSetState({ - providerId: "google", + thirdPartyId: "google", @@ -46,12 +54,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + frontendRedirectURI: "http://localhost/auth/callback/google", }); ``` + - Removed functions - `setStateAndOtherInfoToStorage`, `getAuthorisationURLFromBackend`, `generateStateToSendToOAuthProvider`, `verifyAndGetStateOrThrowError`, `getAuthCodeFromURL`, `getAuthErrorFromURL`, `getAuthStateFromURL` + - In ThirdPartyEmailpassword recipe, + - Changed signatures of the functions `getAuthorisationURLWithQueryParamsAndSetState` + ```diff import { getAuthorisationURLWithQueryParamsAndSetState } from 'supertokens-auth-react/recipe/thirdpartyemailpassword'; - + getAuthorisationURLWithQueryParamsAndSetState({ - providerId: "google", + thirdPartyId: "google", @@ -59,12 +71,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + frontendRedirectURI: "http://localhost/auth/callback/google", }); ``` + - Removed functions - `setStateAndOtherInfoToStorage`, `getAuthorisationURLFromBackend`, `generateStateToSendToOAuthProvider`, `verifyAndGetStateOrThrowError`, `getAuthCodeFromURL`, `getAuthErrorFromURL`, `getAuthStateFromURL` + - In ThirdPartyPasswordless recipe, + - Changed signatures of the functions `getThirdPartyAuthorisationURLWithQueryParamsAndSetState` + ```diff import { getAuthorisationURLWithQueryParamsAndSetState } from 'supertokens-auth-react/recipe/thirdpartypasswordless'; - + getAuthorisationURLWithQueryParamsAndSetState({ - providerId: "google", + thirdPartyId: "google", @@ -72,6 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + frontendRedirectURI: "http://localhost/auth/callback/google", }); ``` + - Removed functions - `setThirdPartyStateAndOtherInfoToStorage`, `getAuthorisationURLFromBackend`, `generateThirdPartyStateToSendToOAuthProvider`, `verifyAndGetThirdPartyStateOrThrowError`, `getThirdPartyAuthCodeFromURL`, `getThirdPartyAuthErrorFromURL`, `getThirdPartyAuthStateFromURL` ## [0.33.1] - 2023-06-08 diff --git a/examples/for-tests-react-16/src/testContext.js b/examples/for-tests-react-16/src/testContext.js index e5b2f8be9..618e990ba 100644 --- a/examples/for-tests-react-16/src/testContext.js +++ b/examples/for-tests-react-16/src/testContext.js @@ -13,7 +13,7 @@ export function getTestContext() { mockLoginMethodsForDynamicLogin: localStorage.getItem("mockLoginMethodsForDynamicLogin"), staticProviderList: localStorage.getItem("staticProviderList"), mockTenantId: localStorage.getItem("mockTenantId"), - clientType: localStorage.getItem("clientType"), + clientType: localStorage.getItem("clientType") || undefined, }; return ret; } diff --git a/examples/for-tests/src/testContext.js b/examples/for-tests/src/testContext.js index e5b2f8be9..618e990ba 100644 --- a/examples/for-tests/src/testContext.js +++ b/examples/for-tests/src/testContext.js @@ -13,7 +13,7 @@ export function getTestContext() { mockLoginMethodsForDynamicLogin: localStorage.getItem("mockLoginMethodsForDynamicLogin"), staticProviderList: localStorage.getItem("staticProviderList"), mockTenantId: localStorage.getItem("mockTenantId"), - clientType: localStorage.getItem("clientType"), + clientType: localStorage.getItem("clientType") || undefined, }; return ret; } diff --git a/lib/build/thirdparty-shared.js b/lib/build/thirdparty-shared.js index adbf4c04d..fa9fa1e1a 100644 --- a/lib/build/thirdparty-shared.js +++ b/lib/build/thirdparty-shared.js @@ -1225,7 +1225,7 @@ function matchRecipeIdUsingState(recipe, userContext) { } function redirectToThirdPartyLogin(input) { return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - var loginMethods, providers, provider, response; + var loginMethods, tenantProviders, providers, provider, response; return genericComponentOverrideContext.__generator(this, function (_a) { switch (_a.label) { case 0: @@ -1239,11 +1239,13 @@ function redirectToThirdPartyLogin(input) { ]; case 1: loginMethods = _a.sent(); + tenantProviders = ( + loginMethods === null || loginMethods === void 0 ? void 0 : loginMethods.thirdparty.enabled + ) + ? loginMethods.thirdparty.providers + : []; providers = mergeProviders({ - tenantProviders: - loginMethods === null || loginMethods === void 0 - ? void 0 - : loginMethods.thirdparty.providers, + tenantProviders: tenantProviders, clientProviders: input.config.signInAndUpFeature.providers, }); provider = providers.find(function (p) { diff --git a/lib/build/thirdparty-shared2.js b/lib/build/thirdparty-shared2.js index a1de4e5eb..bf26d1480 100644 --- a/lib/build/thirdparty-shared2.js +++ b/lib/build/thirdparty-shared2.js @@ -346,7 +346,9 @@ function useChildProps(recipe$1) { if (dynamicLoginMethods.loaded === false) { throw new Error("Component requiring dynamicLoginMethods rendered without FeatureWrapper."); } else { - tenantProviders = dynamicLoginMethods.loginMethods.thirdparty.providers; + tenantProviders = dynamicLoginMethods.loginMethods.thirdparty.enabled + ? dynamicLoginMethods.loginMethods.thirdparty.providers + : []; } } return { diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index 48f49de53..2da25307c 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1 +1 @@ -export declare const package_version = "0.34.1"; +export declare const package_version = "0.34.2"; diff --git a/lib/ts/recipe/thirdparty/components/features/signInAndUp/index.tsx b/lib/ts/recipe/thirdparty/components/features/signInAndUp/index.tsx index 42c1bf840..c1187d881 100644 --- a/lib/ts/recipe/thirdparty/components/features/signInAndUp/index.tsx +++ b/lib/ts/recipe/thirdparty/components/features/signInAndUp/index.tsx @@ -92,7 +92,9 @@ export function useChildProps(recipe: Recipe | undefined): ThirdPartySignInUpChi if (dynamicLoginMethods.loaded === false) { throw new Error("Component requiring dynamicLoginMethods rendered without FeatureWrapper."); } else { - tenantProviders = dynamicLoginMethods.loginMethods.thirdparty.providers; + tenantProviders = dynamicLoginMethods.loginMethods.thirdparty.enabled + ? dynamicLoginMethods.loginMethods.thirdparty.providers + : []; } } diff --git a/lib/ts/recipe/thirdparty/utils.ts b/lib/ts/recipe/thirdparty/utils.ts index a4f5a798c..679095ba7 100644 --- a/lib/ts/recipe/thirdparty/utils.ts +++ b/lib/ts/recipe/thirdparty/utils.ts @@ -141,8 +141,9 @@ export async function redirectToThirdPartyLogin(input: { const loginMethods = await Multitenancy.getInstanceOrThrow().getCurrentDynamicLoginMethods({ userContext: input.userContext, }); + const tenantProviders = loginMethods?.thirdparty.enabled ? loginMethods.thirdparty.providers : []; const providers = mergeProviders({ - tenantProviders: loginMethods?.thirdparty.providers, + tenantProviders, clientProviders: input.config.signInAndUpFeature.providers, }); diff --git a/lib/ts/version.ts b/lib/ts/version.ts index e69df9e08..32897ee40 100644 --- a/lib/ts/version.ts +++ b/lib/ts/version.ts @@ -12,4 +12,4 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const package_version = "0.34.1"; +export const package_version = "0.34.2"; diff --git a/package-lock.json b/package-lock.json index 513890b3b..0737d9dad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-auth-react", - "version": "0.34.1", + "version": "0.34.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-auth-react", - "version": "0.34.1", + "version": "0.34.2", "license": "Apache-2.0", "dependencies": { "intl-tel-input": "^17.0.19", @@ -88,7 +88,7 @@ "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0", - "supertokens-web-js": "^0.7.0" + "supertokens-web-js": "^0.7.2" } }, "eslint": { @@ -16585,9 +16585,9 @@ "integrity": "sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==" }, "node_modules/supertokens-web-js": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/supertokens-web-js/-/supertokens-web-js-0.7.0.tgz", - "integrity": "sha512-+ojphygeSEpP7D7QJz1Yf9rI+ohwkqd+ONsthrNE+yDhKhf0Y9c3thqYMhODoCiScZFlNOFh0E+NvSPcdt1dvQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/supertokens-web-js/-/supertokens-web-js-0.7.2.tgz", + "integrity": "sha512-8TQFfuDzOkdbP/br7AHI8PJaslAYOVmHKT9rxb+v2cuFXYbZzHKoZeY12C1hepqHED8Cbt+rnIIVb2VUYlzd/w==", "peer": true, "dependencies": { "supertokens-js-override": "0.0.4", @@ -30263,9 +30263,9 @@ "integrity": "sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==" }, "supertokens-web-js": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/supertokens-web-js/-/supertokens-web-js-0.7.0.tgz", - "integrity": "sha512-+ojphygeSEpP7D7QJz1Yf9rI+ohwkqd+ONsthrNE+yDhKhf0Y9c3thqYMhODoCiScZFlNOFh0E+NvSPcdt1dvQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/supertokens-web-js/-/supertokens-web-js-0.7.2.tgz", + "integrity": "sha512-8TQFfuDzOkdbP/br7AHI8PJaslAYOVmHKT9rxb+v2cuFXYbZzHKoZeY12C1hepqHED8Cbt+rnIIVb2VUYlzd/w==", "peer": true, "requires": { "supertokens-js-override": "0.0.4", diff --git a/package.json b/package.json index 7bf80d24e..cfb235e6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-auth-react", - "version": "0.34.1", + "version": "0.34.2", "description": "ReactJS SDK that provides login functionality with SuperTokens.", "main": "./index.js", "engines": { @@ -83,7 +83,7 @@ "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0", - "supertokens-web-js": "^0.7.0" + "supertokens-web-js": "^0.7.2" }, "scripts": { "init": "bash ./init.sh", diff --git a/test/end-to-end/multitenancy.dynamic_login_methods.test.js b/test/end-to-end/multitenancy.dynamic_login_methods.test.js index b18a6b947..ee811fe7d 100644 --- a/test/end-to-end/multitenancy.dynamic_login_methods.test.js +++ b/test/end-to-end/multitenancy.dynamic_login_methods.test.js @@ -462,7 +462,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { // Examples from https://supertokens.com/docs/contribute/decisions/multitenancy/0006 - it("should should show emailpassword if it's the only one added on both", async function () { + it("should show emailpassword if it's the only one added on both", async function () { await setEnabledRecipes(page, ["emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -490,7 +490,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, ["email", "password"]); }); - it("should should show thirdpartyemailpassword with emailpassword disabled if FE only has tpep but only thirdparty is enabled", async function () { + it("should show thirdpartyemailpassword with emailpassword disabled if FE only has tpep but only thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdpartyemailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, @@ -518,7 +518,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, []); }); - it("should should show thirdpartyemailpassword if FE has tpep and both emailpassword and thirdparty is enabled", async function () { + it("should show thirdpartyemailpassword if FE has tpep and both emailpassword and thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdpartyemailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -547,7 +547,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); // This is slightly different than the version in the ADR, since it hasn't been updated - it("should should show emailpassword if FE has tp and ep but (no tpep) and both emailpassword and thirdparty is enabled", async function () { + it("should show emailpassword if FE has tp and ep but (no tpep) and both emailpassword and thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -575,7 +575,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, ["email", "password"]); }); - it("should should show thirdparty if FE has tp and pwless and both emailpassword and thirdparty is enabled", async function () { + it("should show thirdparty if FE has tp and pwless and both emailpassword and thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdparty", "passwordless"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -603,7 +603,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, []); }); - it("should should show thirdpartyemailpassword if FE has tpep and ep and both emailpassword and thirdparty is enabled", async function () { + it("should show thirdpartyemailpassword if FE has tpep and ep and both emailpassword and thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdpartyemailpassword", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -631,7 +631,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, ["email", "password"]); }); - it("should should show thirdpartypasswordless if FE has tppwless and ep and both emailpassword and thirdparty is enabled", async function () { + it("should show thirdpartypasswordless if FE has tppwless and ep and both emailpassword and thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdpartypasswordless", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -659,7 +659,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, []); }); - it("should should show thirdpartyemailpassword if FE has tpep and tppwless and all 3 enabled in core", async function () { + it("should show thirdpartyemailpassword if FE has tpep and tppwless and all 3 enabled in core", async function () { await setEnabledRecipes(page, ["thirdpartypasswordless", "thirdpartyemailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -687,7 +687,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, ["email", "password"]); }); - it("should should show thirdpartypwless if rid has FE has tpep and tppwless and all 3 enabled in core", async function () { + it("should show thirdpartypwless if rid has FE has tpep and tppwless and all 3 enabled in core", async function () { await setEnabledRecipes(page, ["thirdpartypasswordless", "thirdpartyemailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -715,7 +715,109 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, ["email", "password"]); }); - it("should should show something went wrong if logging in with disabled method", async function () { + it("should show thirdpartyemailpassword if FE has only tpep and thirdparty is disbled in core", async function () { + await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await enableDynamicLoginMethods(page, { + emailPassword: { enabled: true }, + passwordless: { enabled: true }, + thirdParty: { + enabled: false, + providers: [], + }, + }); + + await Promise.all([ + page.goto(`${TEST_CLIENT_BASE_URL}${DEFAULT_WEBSITE_BASE_PATH}`), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + + // Thirdparty + const providers = await getProvidersLabels(page); + assert.strictEqual(providers.length, 0); + assert.strictEqual(await getProviderLogoCount(page), 0); + + // Emailpassword + const inputNames = await getInputNames(page); + assert.deepStrictEqual(inputNames, ["email", "password"]); + }); + + it("should show thirdpartyemailpassword if FE has only tpep and thirdparty is disbled in core", async function () { + await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await enableDynamicLoginMethods(page, { + emailPassword: { enabled: false }, + passwordless: { enabled: false }, + thirdParty: { + enabled: true, + providers: [], + }, + }); + + await Promise.all([ + page.goto(`${TEST_CLIENT_BASE_URL}${DEFAULT_WEBSITE_BASE_PATH}`), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + + // Thirdparty + const providers = await getProvidersLabels(page); + assert.notStrictEqual(providers.length, 0); + assert.notStrictEqual(await getProviderLogoCount(page), 0); + + // Emailpassword + const inputNames = await getInputNames(page); + assert.deepStrictEqual(inputNames, []); + }); + + it("should show thirdpartpasswordless if FE has only tppwless and thirdparty is disbled in core", async function () { + await setEnabledRecipes(page, ["thirdpartypasswordless"]); + await enableDynamicLoginMethods(page, { + emailPassword: { enabled: true }, + passwordless: { enabled: true }, + thirdParty: { + enabled: false, + providers: [], + }, + }); + + await Promise.all([ + page.goto(`${TEST_CLIENT_BASE_URL}${DEFAULT_WEBSITE_BASE_PATH}`), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + + // Thirdparty + const providers = await getProvidersLabels(page); + assert.strictEqual(providers.length, 0); + assert.strictEqual(await getProviderLogoCount(page), 0); + + // pwless + await waitForSTElement(page, "[data-supertokens~=input][name=emailOrPhone]"); + }); + + it("should show thirdpartpasswordless if FE has only tppwless and passwordless is disbled in core", async function () { + await setEnabledRecipes(page, ["thirdpartypasswordless"]); + await enableDynamicLoginMethods(page, { + emailPassword: { enabled: false }, + passwordless: { enabled: false }, + thirdParty: { + enabled: true, + providers: [], + }, + }); + + await Promise.all([ + page.goto(`${TEST_CLIENT_BASE_URL}${DEFAULT_WEBSITE_BASE_PATH}`), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + + // Thirdparty + const providers = await getProvidersLabels(page); + assert.notStrictEqual(providers.length, 0); + assert.notStrictEqual(await getProviderLogoCount(page), 0); + + // pwless + await waitForSTElement(page, "[data-supertokens~=input][name=emailOrPhone]", true); + }); + + it("should show something went wrong if logging in with disabled method", async function () { await setEnabledRecipes(page, ["emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, @@ -753,7 +855,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.strictEqual(error, SOMETHING_WENT_WRONG_ERROR); }); - it("should should show thirdparty if FE has tp and pwless and both emailpassword and thirdparty is enabled", async function () { + it("should show thirdparty if FE has tp and pwless and both emailpassword and thirdparty is enabled", async function () { await setEnabledRecipes(page, ["thirdparty", "passwordless"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, @@ -781,7 +883,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { assert.deepStrictEqual(inputNames, []); }); - it("should should be able to log in with dynamically added tp providers", async function () { + it("should be able to log in with dynamically added tp providers", async function () { await setEnabledRecipes(page, ["thirdparty"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, @@ -885,13 +987,6 @@ export async function enableDynamicLoginMethods(page, mockLoginMethods, tenantId assert.strictEqual(coreResp.status, 200); } - coreResp = await fetch(`http://localhost:9000/appid-${app}/${tenantId}/recipe/multitenancy/tenant`, { - method: "GET", - headers: new Headers([ - ["content-type", "application/json"], - ["rid", "multitenancy"], - ]), - }); return page.evaluate(() => { window.localStorage.setItem("usesDynamicLoginMethods", "true");