Skip to content

Commit

Permalink
feat: multitenancy (#677)
Browse files Browse the repository at this point in the history
* init web-js recipes, supertokens from auth-react

* move recipe output type to types.ts

* make combined recipes use web-js version

* get rid of recipeImplementation for single recipes

* add functionOverrides to thirdpartypasswordless recipe

* add functionOverrides to thirdpartyemailpassword recipe

* get rid of one the duplicate setLoginAttemptInfo calls

* fix tests

* update changelog

* fixes after review

* move initOutput type to root types

* fix tests

* review fixes

* review fixes

* add build files

* use normalisedConfig for webjs init in combined recipes

* remove commented code

* disable isIE check in order to avoid error

* test fixes caused by utils

* fix typo

* rename recipeImpl -> webJSRecipe for all recipes

* add comments

* utils-> normalization_function  fix

* type changes for recipe member

* type change, renamein

* move config normalization to init for session

* Update thirdpartypasswordless comments

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* Update thirdpartyemailpassword/recipe comments

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* review fixes

* reuse action type for RecipeInitResult

* move onHandelEvent call to combined recipe overrides

* fixes-session, overrides

* add callbacks to isVerified claim

* fix exampleTestHelper

* fix build folder

* add auth-react boolean claim

* extend web-js claims

* fix tests, rename callbacks

* review fixes

* fix tests

* use web-js import instead of website

* fix type GetRedirectionURLContext, add comment, pass context to getGlobalClaim function calls

* fix duplication

* move helper function to utils

* fix emailverification wrapper type

* self review fixes

* second round review fixed

* add access-denied page

* add ability to override and change style of access-denied screen

* add denial info to access denied page

* update example apps

* fix rollup watch mac issue

* review fixes

* fix redirection priorities

* move cliam functions to session utils

* fix util function

* add unit tests for new utils function

* initial splitting

* create class for each recipe to handle routes

* review fixes for /lib

* update examples

* fix partially sessionAuth tests

* fix: typo

* fix: sessionAuth test

* update changelog

* fix lintiing issues

* fix tests

* Merge branch '0.31' into 'feat/splitting-prebuild-ui-components'

* fix: partially unit test

* fix: unit test case

* fix: broken components overriding after merge

* fix: move themeBase & fix example app

* fix: type fix

* fix: access denial info

* fix: exporting claims from auth-react

* fix: review fixes

* fix: by review comments

* fix: adjust access-denied page

* more review fixes

* fix: review fixes

* change implementataion & access denied page design

* fix: default page & review changes

* fix: default failed claim when none has callback

* css: override container width

* fix: review comments

* fix: add comment

* fix: respect rid when rendering UIs

* fix tests

* fix: add underscore prefix

* test: add test for back button

* fix: emailverification prebuilt ui

* fix: review changes

* fix no router case

* fix: prebuiltui components types exoprt

* fix: emailverification exports

* fix: test apps

* fix: adjust routing component to handle duplicate renders

* feat: clean up onSuccessRedirection and add showAccessDeniedOnFailure

* fix: e2e tests

* update changelog

* fix: example apps

* fix examples & changelog

* fix: preBuiltUI -> prebuiltui

* fix filename casing

* fix: preBuiltUI -> prebuiltui

* fix filename casing

* expose signle routing functons

* rename: prebuiltui->ui

* update test apps

* fix: unit tests

* preBuiltUI -> prebuiltui

* update changelog

* update example apps

* getSuperTokensReactRouterDomRoutes casing fix

* getSuperTokensReactRouterDomRoutes casing fix

* review fixes

* review fixes

* review fixes

* wip

* fix: review comments(preBuiltUI interface consistency)

* parent c3935c9
author Alisher <alisher@supertokens.com> 1679996212 +0400
committer Alisher <alisher@supertokens.com> 1682072680 +0400

feat: multitenancy

* feat: thirdparty rework (#676)

* add missing providers & add prop to buttonComponent

* fix: Thirdparty login popup (#675)

* fix: thirdparty popup example

* chore: changelog

* fix: adding backend

* fix: pr comment

* fix: pr comment

* fix: pr comment

* fix: pr comment

* feat: add new providers & add prop to buttonComponent

* fix: activeDirectory width

* update changelog

* Update examples/with-thirdparty-popup/README.md

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* fix: providers

* fix: move getButton to base class

* fix: review comments

* Update examples/with-thirdparty-popup/frontend/src/App.tsx

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* fixes

---------

Co-authored-by: Alisher <alisher@supertokens.com>
Co-authored-by: Sattvik Chakravarthy <sattvik@gmail.com>
Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* Update lib/ts/recipe/multitenancy/index.ts

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* fix: review comments

* revert providers form

* fix: review 2nd round

* fixes: getRoutingComponent

* fix: review 2nd round & embedding components fixes

* fix: review

* fix: move provider picking logic into separate wrapper

* fix: remove test data

* fix: picking providers logic & types

* fix: type fixes & move helper to utils

* fix: add getter for dynamicLoginMethods

* fix: provider selection algorithm

* fixes

* fixes

* remove memoization of providders

* fixes

* chore: minor cleanup/test fixes + migration docs

* test: revert merge issue

* fixes

* fixes

* fixes

* fixes

* fixes

* fixes

* fixes reciperouter

* fixes

* fix: update to match ADRs & new BE version

* test: multitenancy (#678)

* wip: tests

* test: additional tests

* fix: picking providers logic & types

* test: adds test case

* fix test & remove redundant test case

* fix: minor cleanup/small fix + test extension

---------

Co-authored-by: Alisher <alisher@supertokens.com>
Co-authored-by: Mihaly Lengyel <mihaly@lengyel.tech>

* feat: update to latest interfaces and fix tests

* test: updated test asserts to match new behaviour

* fix: make the interface more consistent with web-js

* feat: improve typing of getClaimValue

* feat: update tests to use core + small fixes

* test: add tests for tenant id exceptions

* fix: fixed handling empty provider arrays + missing provider

* fix: fix tests

* fix: self-review fixes

* fix: self-review fixes & chores

* test: fix react 16 test app

* docs: update examples

---------

Co-authored-by: Alisher <alisher@supertokens.com>
Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>
Co-authored-by: Sattvik Chakravarthy <sattvik@gmail.com>
  • Loading branch information
4 people committed Jul 19, 2023
1 parent 0343521 commit 8fcfd18
Show file tree
Hide file tree
Showing 240 changed files with 23,812 additions and 31,612 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.34.0] - 2023-07-18

### Changes

- Added Active Directory, Okta, Linked In, Boxy SAML, Google Workspaces providers
- Added name prop to buttonComponent
- Added thirdparty login with popup window
- Added Multitenancy recipe
- Improved typing of `getClaimValue`
- Optional `clientType` config in the input for `SuperTokens.init` function, that is used by thirdparty and multitenancy recipes.
- Optional `usesDynamicLoginMethods` config in the input for `SuperTokens.init` function, which makes the SDK check the backend to see which recipes should be enabled
- Added an overrideable `getTenantIdFromURL` to multiple recipes

### Breaking Changes

- Only supporting FDI 1.17
- 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`
- Removed functions - `setStateAndOtherInfoToStorage`, `getAuthorisationURLFromBackend`, `generateStateToSendToOAuthProvider`, `verifyAndGetStateOrThrowError`, `getAuthCodeFromURL`, `getAuthErrorFromURL`, `getAuthStateFromURL`
- In ThirdPartyEmailpassword recipe,
- Changed signatures of the functions `getAuthorisationURLWithQueryParamsAndSetState`
- Removed functions - `setStateAndOtherInfoToStorage`, `getAuthorisationURLFromBackend`, `generateStateToSendToOAuthProvider`, `verifyAndGetStateOrThrowError`, `getAuthCodeFromURL`, `getAuthErrorFromURL`, `getAuthStateFromURL`
- In ThirdPartyPasswordless recipe,
- Changed signatures of the functions `getThirdPartyAuthorisationURLWithQueryParamsAndSetState`
- Removed functions - `setThirdPartyStateAndOtherInfoToStorage`, `getAuthorisationURLFromBackend`, `generateThirdPartyStateToSendToOAuthProvider`, `verifyAndGetThirdPartyStateOrThrowError`, `getThirdPartyAuthCodeFromURL`, `getThirdPartyAuthErrorFromURL`, `getThirdPartyAuthStateFromURL`

## [0.33.1] - 2023-06-08

### Fixes
Expand Down
179 changes: 101 additions & 78 deletions examples/for-tests-react-16/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ThirdParty from "supertokens-auth-react/recipe/thirdparty";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import ThirdPartyPasswordless from "supertokens-auth-react/recipe/thirdpartypasswordless";
import UserRoles from "supertokens-auth-react/recipe/userroles";
import MultiTenancy from "supertokens-auth-react/recipe/multitenancy";

import axios from "axios";
import { useSessionContext } from "supertokens-auth-react/recipe/session";
Expand All @@ -25,6 +26,7 @@ import HeliumTheme from "./Themes/Helium";
import HydrogenTheme from "./Themes/Hydrogen";
import { logWithPrefix } from "./logWithPrefix";
import { ErrorBoundary } from "./ErrorBoundary";
import { getTestContext, getEnabledRecipes, getQueryParams } from "./testContext";
let { useNavigate } = require("react-router-dom");
let withRouter = undefined;
const loadv5RRD = window.localStorage.getItem("react-router-dom-is-v5") === "true";
Expand Down Expand Up @@ -173,7 +175,31 @@ const formFields = [
},
];

const testContext = getTestContext();

let recipeList = [
MultiTenancy.init({
override: {
functions: (oI) => ({
...oI,
getTenantId: (input) => {
if (testContext.mockTenantId) {
return testContext.mockTenantId;
}

return oI.getTenantId(input);
},
getLoginMethods: (input) => {
if (testContext.mockLoginMethodsForDynamicLogin) {
return {
...JSON.parse(testContext.mockLoginMethodsForDynamicLogin),
};
}
return oI.getLoginMethods(input);
},
}),
},
}),
Session.init({
override: {
functions: (implementation) => {
Expand Down Expand Up @@ -223,29 +249,29 @@ let recipeList = [
}),
];

const testContext = {
disableDefaultUI: getQueryParams("disableDefaultUI") === "true",
thirdPartyRedirectURL: localStorage.getItem("thirdPartyRedirectURL"),
};
let enabledRecipes = getEnabledRecipes();

if (authRecipe === "thirdparty") {
if (enabledRecipes.includes("thirdparty")) {
recipeList = [getThirdPartyConfigs(testContext), ...recipeList];
} else if (authRecipe === "emailpassword") {
}
if (enabledRecipes.includes("emailpassword")) {
recipeList = [getEmailPasswordConfigs(testContext), ...recipeList];
} else if (authRecipe === "both") {
recipeList = [getEmailPasswordConfigs(testContext), getThirdPartyConfigs(testContext), ...recipeList];
} else if (authRecipe === "thirdpartyemailpassword") {
}
if (enabledRecipes.includes("thirdpartyemailpassword")) {
recipeList = [getThirdPartyEmailPasswordConfigs(testContext), ...recipeList];
} else if (authRecipe === "passwordless") {
}
if (enabledRecipes.includes("passwordless")) {
recipeList = [getPasswordlessConfigs(testContext), ...recipeList];
} else if (authRecipe === "thirdpartypasswordless") {
}
if (enabledRecipes.includes("thirdpartypasswordless")) {
recipeList = [getThirdPartyPasswordlessConfigs(testContext), ...recipeList];
}

if (emailVerificationMode !== "OFF") {
recipeList.push(getEmailVerificationConfigs(testContext));
}
SuperTokens.init({
usesDynamicLoginMethods: testContext.usesDynamicLoginMethods,
appInfo: {
appName: "SuperTokens",
websiteDomain: getWebsiteDomain(),
Expand Down Expand Up @@ -305,11 +331,6 @@ function App() {
}
}

function getQueryParams(param) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(param);
}

export default App;

export function BaseComponent({ children }) {
Expand Down Expand Up @@ -616,7 +637,27 @@ function getEmailPasswordConfigs({ disableDefaultUI }) {
});
}

function getThirdPartyPasswordlessConfigs({ disableDefaultUI, thirdPartyRedirectURL }) {
function getThirdPartyPasswordlessConfigs({ staticProviderList, disableDefaultUI, thirdPartyRedirectURL }) {
let providers = [
ThirdParty.Github.init(),
ThirdParty.Google.init(),
ThirdParty.Facebook.init(),
ThirdParty.Apple.init(),
{
id: "custom",
name: "Custom",
},
{
id: "auth0",
name: "Auth0",
getRedirectURL: thirdPartyRedirectURL !== null ? () => thirdPartyRedirectURL : undefined,
},
];
if (staticProviderList) {
const ids = JSON.parse(staticProviderList);
providers = ids.map((id) => providers.find((p) => p.id === id) || { id, name: id });
}

return ThirdPartyPasswordless.init({
override: {
functions: (implementation) => {
Expand Down Expand Up @@ -706,21 +747,7 @@ function getThirdPartyPasswordlessConfigs({ disableDefaultUI, thirdPartyRedirect
`,
privacyPolicyLink: "https://supertokens.io/legal/privacy-policy",
termsOfServiceLink: "https://supertokens.io/legal/terms-and-conditions",
providers: [
ThirdPartyEmailPassword.Github.init(),
ThirdPartyEmailPassword.Google.init(),
ThirdPartyEmailPassword.Facebook.init(),
ThirdPartyEmailPassword.Apple.init(),
{
id: "custom",
name: "Custom",
},
{
id: "auth0",
name: "Auth0",
getRedirectURL: thirdPartyRedirectURL !== null ? () => thirdPartyRedirectURL : undefined,
},
],
providers,
defaultCountry: passwordlessDefaultCountry,
resendEmailOrSMSGapInSeconds: 2,
guessInternationPhoneNumberFromInputPhoneNumber: passwordlessDisablePhoneGuess
Expand Down Expand Up @@ -812,7 +839,26 @@ function getPasswordlessConfigs({ disableDefaultUI }) {
});
}

function getThirdPartyConfigs({ disableDefaultUI, thirdPartyRedirectURL }) {
function getThirdPartyConfigs({ staticProviderList, disableDefaultUI, thirdPartyRedirectURL }) {
let providers = [
ThirdParty.Github.init(),
ThirdParty.Google.init(),
ThirdParty.Facebook.init(),
ThirdParty.Apple.init(),
{
id: "custom",
name: "Custom",
},
{
id: "auth0",
name: "Auth0",
getRedirectURL: thirdPartyRedirectURL !== null ? () => thirdPartyRedirectURL : undefined,
},
];
if (staticProviderList) {
const ids = JSON.parse(staticProviderList);
providers = ids.map((id) => providers.find((p) => p.id === id) || { id, name: id });
}
return ThirdParty.init({
style: `
[data-supertokens~=container] {
Expand Down Expand Up @@ -869,21 +915,7 @@ function getThirdPartyConfigs({ disableDefaultUI, thirdPartyRedirectURL }) {
style: theme.style,
privacyPolicyLink: "https://supertokens.com/legal/privacy-policy",
termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions",
providers: [
ThirdParty.Github.init(),
ThirdParty.Google.init(),
ThirdParty.Facebook.init(),
ThirdParty.Apple.init(),
{
id: "custom",
name: "Custom",
},
{
id: "auth0",
name: "Auth0",
getRedirectURL: thirdPartyRedirectURL !== null ? () => thirdPartyRedirectURL : undefined,
},
],
providers,
},

oAuthCallbackScreen: {
Expand All @@ -892,7 +924,26 @@ function getThirdPartyConfigs({ disableDefaultUI, thirdPartyRedirectURL }) {
});
}

function getThirdPartyEmailPasswordConfigs({ disableDefaultUI, thirdPartyRedirectURL }) {
function getThirdPartyEmailPasswordConfigs({ staticProviderList, disableDefaultUI, thirdPartyRedirectURL }) {
let providers = [
ThirdParty.Github.init(),
ThirdParty.Google.init(),
ThirdParty.Facebook.init(),
ThirdParty.Apple.init(),
{
id: "custom",
name: "Custom",
},
{
id: "auth0",
name: "Auth0",
getRedirectURL: thirdPartyRedirectURL !== null ? () => thirdPartyRedirectURL : undefined,
},
];
if (staticProviderList) {
const ids = JSON.parse(staticProviderList);
providers = ids.map((id) => providers.find((p) => p.id === id) || { id, name: id });
}
return ThirdPartyEmailPassword.init({
preAPIHook: async (context) => {
console.log(`ST_LOGS THIRD_PARTY_EMAIL_PASSWORD PRE_API_HOOKS ${context.action}`);
Expand Down Expand Up @@ -1026,20 +1077,6 @@ function getThirdPartyEmailPasswordConfigs({ disableDefaultUI, thirdPartyRedirec

return implementation.verifyAndGetStateOrThrowError(input);
},
getAuthCodeFromURL(input) {
if (input.userContext["key"] !== undefined) {
log(`GET_AUTH_CODE_FROM_URL RECEIVED_USER_CONTEXT`);
}

return implementation.getAuthCodeFromURL(input);
},
getAuthErrorFromURL(input) {
if (input.userContext["key"] !== undefined) {
log(`GET_AUTH_ERROR_FROM_URL RECEIVED_USER_CONTEXT`);
}

return implementation.getAuthErrorFromURL(input);
},
};
},
},
Expand All @@ -1056,21 +1093,7 @@ function getThirdPartyEmailPasswordConfigs({ disableDefaultUI, thirdPartyRedirec
termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions",
},
style: theme.style,
providers: [
ThirdPartyEmailPassword.Github.init(),
ThirdPartyEmailPassword.Google.init(),
ThirdPartyEmailPassword.Facebook.init(),
ThirdPartyEmailPassword.Apple.init(),
{
id: "custom",
name: "Custom",
},
{
id: "auth0",
name: "Auth0",
getRedirectURL: thirdPartyRedirectURL !== null ? () => thirdPartyRedirectURL : undefined,
},
],
providers,
},
disableEmailPassword: false,

Expand Down
31 changes: 17 additions & 14 deletions examples/for-tests-react-16/src/AppWithReactDomRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,27 @@ import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpass
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";
import { AccessDeniedScreen } from "supertokens-auth-react/recipe/session/prebuiltui";
import { getEnabledRecipes } from "./testContext";

function AppWithReactDomRouter(props) {
const authRecipe = window.localStorage.getItem("authRecipe") || "emailpassword";
const enabledRecipes = getEnabledRecipes();
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

let recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
if (authRecipe === "thirdparty") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI];
} else if (authRecipe === "emailpassword") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
} else if (authRecipe === "both") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI, ThirdPartyPreBuiltUI];
} else if (authRecipe === "thirdpartyemailpassword") {
recipePreBuiltUIList = [ThirdPartyEmailPasswordPreBuiltUI];
} else if (authRecipe === "passwordless") {
recipePreBuiltUIList = [PasswordlessPreBuiltUI];
} else if (authRecipe === "thirdpartypasswordless") {
recipePreBuiltUIList = [ThirdPartyPasswordlessPreBuiltUI];
let recipePreBuiltUIList = [];
if (enabledRecipes.includes("emailpassword")) {
recipePreBuiltUIList.push(EmailPasswordPreBuiltUI);
}
if (enabledRecipes.includes("thirdparty")) {
recipePreBuiltUIList.push(ThirdPartyPreBuiltUI);
}
if (enabledRecipes.includes("thirdpartyemailpassword")) {
recipePreBuiltUIList.push(ThirdPartyEmailPasswordPreBuiltUI);
}
if (enabledRecipes.includes("passwordless")) {
recipePreBuiltUIList.push(PasswordlessPreBuiltUI);
}
if (enabledRecipes.includes("thirdpartypasswordless")) {
recipePreBuiltUIList.push(ThirdPartyPasswordlessPreBuiltUI);
}

if (emailVerificationMode !== "OFF") {
Expand Down
32 changes: 18 additions & 14 deletions examples/for-tests-react-16/src/AppWithReactDomRouterV5.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,29 @@ import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpass
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";
import { AccessDeniedScreen } from "supertokens-auth-react/recipe/session/prebuiltui";
import { getEnabledRecipes } from "./testContext";

function AppWithReactDomRouter(props) {
const authRecipe = window.localStorage.getItem("authRecipe") || "emailpassword";
const enabledRecipes = getEnabledRecipes();
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

let recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
if (authRecipe === "thirdparty") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI];
} else if (authRecipe === "emailpassword") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
} else if (authRecipe === "both") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI, ThirdPartyPreBuiltUI];
} else if (authRecipe === "thirdpartyemailpassword") {
recipePreBuiltUIList = [ThirdPartyEmailPasswordPreBuiltUI];
} else if (authRecipe === "passwordless") {
recipePreBuiltUIList = [PasswordlessPreBuiltUI];
} else if (authRecipe === "thirdpartypasswordless") {
recipePreBuiltUIList = [ThirdPartyPasswordlessPreBuiltUI];
let recipePreBuiltUIList = [];
if (enabledRecipes.includes("emailpassword")) {
recipePreBuiltUIList.push(EmailPasswordPreBuiltUI);
}
if (enabledRecipes.includes("thirdparty")) {
recipePreBuiltUIList.push(ThirdPartyPreBuiltUI);
}
if (enabledRecipes.includes("thirdpartyemailpassword")) {
recipePreBuiltUIList.push(ThirdPartyEmailPasswordPreBuiltUI);
}
if (enabledRecipes.includes("passwordless")) {
recipePreBuiltUIList.push(PasswordlessPreBuiltUI);
}
if (enabledRecipes.includes("thirdpartypasswordless")) {
recipePreBuiltUIList.push(ThirdPartyPasswordlessPreBuiltUI);
}

if (emailVerificationMode !== "OFF") {
recipePreBuiltUIList.push(EmailVerificationPreBuiltUI);
}
Expand Down

0 comments on commit 8fcfd18

Please sign in to comment.