diff --git a/packages/login/src/app/(root)/consent/page.tsx b/packages/login/src/app/(root)/consent/page.tsx index cf48345a32..a63ebdba08 100644 --- a/packages/login/src/app/(root)/consent/page.tsx +++ b/packages/login/src/app/(root)/consent/page.tsx @@ -24,7 +24,7 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode -import { IClientProps } from "@docspace/shared/utils/oauth/interfaces"; +import { IClientProps } from "@docspace/shared/utils/oauth/types"; import Consent from "@/components/Consent"; import { getOAuthClient, getScopeList, getUser } from "@/utils/actions"; @@ -36,7 +36,7 @@ async function Page({ }) { const clientId = searchParams.clientId ?? searchParams.client_id; const [client, scopes, user] = await Promise.all([ - getOAuthClient(clientId, true), + getOAuthClient(clientId), getScopeList(), getUser(), ]); diff --git a/packages/login/src/app/(root)/layout.tsx b/packages/login/src/app/(root)/layout.tsx index 438ab351bd..b96500fde2 100644 --- a/packages/login/src/app/(root)/layout.tsx +++ b/packages/login/src/app/(root)/layout.tsx @@ -83,7 +83,7 @@ export default async function Layout({ - {children} + {children} diff --git a/packages/login/src/app/(root)/page.tsx b/packages/login/src/app/(root)/page.tsx index 187d2fd6be..437e66904a 100644 --- a/packages/login/src/app/(root)/page.tsx +++ b/packages/login/src/app/(root)/page.tsx @@ -24,58 +24,56 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode -import { INoAuthClientProps } from "@docspace/shared/utils/oauth/interfaces"; +import { INoAuthClientProps } from "@docspace/shared/utils/oauth/types"; -import { getConfig, getOAuthClient, getSettings } from "@/utils/actions"; +import { getOAuthClient, getSettings } from "@/utils/actions"; import Login from "@/components/Login"; import LoginForm from "@/components/LoginForm"; import ThirdParty from "@/components/ThirdParty"; import RecoverAccess from "@/components/RecoverAccess"; import Register from "@/components/Register"; +import { FormWrapper } from "@docspace/shared/components/form-wrapper"; async function Page({ searchParams, }: { searchParams: { [key: string]: string }; }) { - const clientId = searchParams.clientId; + const clientId = searchParams.client_id; - const [settings, client, config] = await Promise.all([ + const [settings, client] = await Promise.all([ getSettings(), - clientId ? getOAuthClient(clientId, false) : undefined, - clientId ? getConfig() : undefined, + clientId ? getOAuthClient(clientId) : undefined, ]); - const isPublicOAuth = clientId && config.oauth2.publicClient; - - console.log(isPublicOAuth); - return ( - - {settings && typeof settings !== "string" && ( - <> - - {!clientId && } - {settings.enableAdmMess && } - {settings.enabledJoin && !clientId && ( - + + {settings && typeof settings !== "string" && ( + <> + - )} - - )} - + {!clientId && } + {settings.enableAdmMess && } + {settings.enabledJoin && !clientId && ( + + )} + + )} + + ); } diff --git a/packages/login/src/app/(root)/tenant-list/page.tsx b/packages/login/src/app/(root)/tenant-list/page.tsx new file mode 100644 index 0000000000..a187410f32 --- /dev/null +++ b/packages/login/src/app/(root)/tenant-list/page.tsx @@ -0,0 +1,23 @@ +import TenantList from "@/components/TenantList"; +import { getSettings } from "@/utils/actions"; + +export default async function Page({ + searchParams, +}: { + searchParams: { [key: string]: string }; +}) { + const settings = await getSettings(); + + const { portals } = JSON.parse(searchParams.portals); + const clientId = searchParams.clientId; + + if (typeof settings !== "object") return; + + return ( + + ); +} diff --git a/packages/login/src/components/Consent.tsx b/packages/login/src/components/Consent.tsx index c2c7aff5cd..68a11f7600 100644 --- a/packages/login/src/components/Consent.tsx +++ b/packages/login/src/components/Consent.tsx @@ -41,12 +41,13 @@ import { AvatarSize, } from "@docspace/shared/components/avatar"; import { deleteCookie } from "@docspace/shared/utils/cookie"; -import { IClientProps, IScope } from "@docspace/shared/utils/oauth/interfaces"; +import { IClientProps, IScope } from "@docspace/shared/utils/oauth/types"; import { TUser } from "@docspace/shared/api/people/types"; import api from "@docspace/shared/api"; import OAuthClientInfo from "./ConsentInfo"; import { useRouter } from "next/navigation"; +import { FormWrapper } from "@docspace/shared/components/form-wrapper"; const StyledButtonContainer = styled.div` margin-top: 32px; @@ -158,11 +159,11 @@ const Consent = ({ client, scopes, user }: IConsentProps) => { const onChangeUserClick = async () => { await api.user.logout(); - router.push(`/?clientId=${client.clientId}`); + router.push(`/?client_id=${client.clientId}&type=oauth2`); }; return ( - <> + { - Data shared with {{ displayName: self.displayName }} will be + Data shared with {{ displayName: user.displayName }} will be governed by {{ nameApp: client.name }} { - + ); }; diff --git a/packages/login/src/components/GreetingContainer.tsx b/packages/login/src/components/GreetingContainer.tsx index 2b7fc14e94..8feab34b44 100644 --- a/packages/login/src/components/GreetingContainer.tsx +++ b/packages/login/src/components/GreetingContainer.tsx @@ -29,7 +29,7 @@ import React, { useLayoutEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; -import { useSearchParams } from "next/navigation"; +import { usePathname, useSearchParams } from "next/navigation"; import { useTheme } from "styled-components"; import { Text } from "@docspace/shared/components/text"; @@ -48,6 +48,7 @@ const GreetingContainer = ({ greetingSettings }: GreetingContainersProps) => { const logoUrl = getLogoUrl(WhiteLabelLogoType.LoginPage, !theme.isBase); const searchParams = useSearchParams(); + const pathname = usePathname(); const [invitationLinkData, setInvitationLinkData] = useState({ email: "", @@ -84,7 +85,9 @@ const GreetingContainer = ({ greetingSettings }: GreetingContainersProps) => { textAlign="center" className="greeting-title" > - {greetingSettings} + {pathname === "/tenant-list" + ? "Choose your portal" + : greetingSettings} )} diff --git a/packages/login/src/components/LoginForm/index.tsx b/packages/login/src/components/LoginForm/index.tsx index 95f407dbcd..864391c2c6 100644 --- a/packages/login/src/components/LoginForm/index.tsx +++ b/packages/login/src/components/LoginForm/index.tsx @@ -51,9 +51,10 @@ import { setWithCredentialsStatus } from "@docspace/shared/api/client"; import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types"; import api from "@docspace/shared/api"; import { RecaptchaType } from "@docspace/shared/enums"; +import { getAvailablePortals } from "@docspace/shared/api/management"; import { LoginFormProps } from "@/types"; -import { getEmailFromInvitation } from "@/utils"; +import { generateOAuth2ReferenceURl, getEmailFromInvitation } from "@/utils"; import EmailContainer from "./sub-components/EmailContainer"; import PasswordContainer from "./sub-components/PasswordContainer"; @@ -63,6 +64,7 @@ import LDAPContainer from "./sub-components/LDAPContainer"; import { StyledCaptcha } from "./LoginForm.styled"; import { LoginDispatchContext, LoginValueContext } from "../Login"; import OAuthClientInfo from "../ConsentInfo"; +// import { gitAvailablePortals } from "@/utils/actions"; const LoginForm = ({ hashSettings, @@ -204,7 +206,7 @@ const LoginForm = ({ if (!passwordValid) setPasswordValid(true); }; - const onSubmit = useCallback(() => { + const onSubmit = useCallback(async () => { //errorText && setErrorText(""); let captchaToken: string | undefined | null = ""; @@ -254,6 +256,32 @@ const LoginForm = ({ isDesktop && checkPwd(); const session = !isChecked; + if (client?.isPublic && hash) { + const portals = await getAvailablePortals({ + Email: user, + PasswordHash: hash, + }); + + // if (portals.length === 1) { + // const referenceUrl = generateOAuth2ReferenceURl(client.clientId); + // window.open( + // `${portals[0].portalLink}&referenceUrl=${referenceUrl}`, + // "_self", + // ); + // } + + const searchParams = new URLSearchParams(); + + const portalsString = JSON.stringify({ portals }); + + searchParams.set("portals", portalsString); + searchParams.set("clientId", client.clientId); + + router.push(`/tenant-list?${searchParams.toString()}`); + setIsLoading(false); + return; + } + login(user, hash, pwd, session, captchaToken, currentCulture, reCaptchaType) .then(async (res: string | object) => { if (clientId) { @@ -307,17 +335,18 @@ const LoginForm = ({ password, identifierValid, setIsLoading, + isLdapLoginChecked, hashSettings, isDesktop, isChecked, - isLdapLoginChecked, - + client?.isPublic, + client?.clientId, + currentCulture, + reCaptchaType, isCaptchaSuccessful, + router, clientId, referenceUrl, - currentCulture, - router, - reCaptchaType, ]); const onBlurEmail = () => { diff --git a/packages/login/src/components/TenantList/TenantList.styled.ts b/packages/login/src/components/TenantList/TenantList.styled.ts new file mode 100644 index 0000000000..c9b86fbde6 --- /dev/null +++ b/packages/login/src/components/TenantList/TenantList.styled.ts @@ -0,0 +1,78 @@ +import { mobile } from "@docspace/shared/utils"; +import styled from "styled-components"; + +const StyledTenantList = styled.div` + margin-top: -16px; + + display: flex; + flex-direction: column; + align-items: center; + + .more-accounts { + color: ${(props) => props.theme.text.disableColor}; + text-align: center; + + margin-bottom: 32px; + } + + .items-list { + width: 100%; + max-width: 480px; + + border: 1px solid ${(props) => props.theme.oauth.infoDialog.separatorColor}; + border-radius: 6px; + + div:last-child { + border: none !important; + } + + @media ${mobile} { + maxwidth: 100%; + } + } + + .item { + height: 59px; + + box-sizing: border-box; + + display: flex; + align-items: center; + justify-content: space-between; + + padding: 0 16px; + + border-bottom: 1px solid + ${(props) => props.theme.oauth.infoDialog.separatorColor}; + + :hover { + cursor: pointer; + + background-color: ${(props) => + props.theme.dropDownItem.hoverBackgroundColor}; + } + + .info { + display: flex; + align-items: center; + + max-width: calc(100% - 64px); + } + + .favicon { + width: 32px; + height: 32px; + + margin-right: 12px; + } + + .icon-button { + cursor: pointer; + } + } + .back-button { + margin: 32px auto 0; + } +`; + +export default StyledTenantList; diff --git a/packages/login/src/components/TenantList/TenantList.types.ts b/packages/login/src/components/TenantList/TenantList.types.ts new file mode 100644 index 0000000000..d682b6f0a1 --- /dev/null +++ b/packages/login/src/components/TenantList/TenantList.types.ts @@ -0,0 +1,13 @@ +type TPortal = { portalLink: string; portalName: string }; + +export type TenantListProps = { + baseDomain: string; + clientId: string; + portals: TPortal[]; +}; + +export type ItemProps = { + portal: TPortal; + baseDomain: string; + clientId: string; +}; diff --git a/packages/login/src/components/TenantList/index.tsx b/packages/login/src/components/TenantList/index.tsx new file mode 100644 index 0000000000..de1def0df8 --- /dev/null +++ b/packages/login/src/components/TenantList/index.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { Text } from "@docspace/shared/components/text"; + +import Item from "./sub-components/Item"; + +import StyledTenantList from "./TenantList.styled"; +import { TenantListProps } from "./TenantList.types"; +import { Button } from "@docspace/shared/components/button"; +import { useRouter } from "next/navigation"; + +const TenantList = ({ portals, clientId, baseDomain }: TenantListProps) => { + const router = useRouter(); + + const goToLogin = () => { + router.push(`/?type=oauth2&client_id=${clientId}`); + }; + + return ( + + + You have more than one accounts. Please choose one of them + +
+ {portals.map((item) => ( + + ))} +
+