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) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default TenantList;
diff --git a/packages/login/src/components/TenantList/sub-components/Item.tsx b/packages/login/src/components/TenantList/sub-components/Item.tsx
new file mode 100644
index 0000000000..2c0a4572f5
--- /dev/null
+++ b/packages/login/src/components/TenantList/sub-components/Item.tsx
@@ -0,0 +1,44 @@
+/* eslint-disable @next/next/no-img-element */
+
+import { Text } from "@docspace/shared/components/text";
+
+import ArrowRightSvrUrl from "PUBLIC_DIR/images/arrow.right.react.svg?url";
+
+import { ItemProps } from "../TenantList.types";
+import { IconButton } from "@docspace/shared/components/icon-button";
+import { generateOAuth2ReferenceURl } from "@/utils";
+
+const Item = ({ clientId, portal, baseDomain }: ItemProps) => {
+ console.log(portal);
+ const name = portal.portalName.includes(baseDomain)
+ ? portal.portalName
+ : `${portal.portalName}.${baseDomain}`;
+
+ const onClick = () => {
+ const referenceUrl = generateOAuth2ReferenceURl(clientId);
+
+ window.open(`${portal.portalLink}&referenceUrl=${referenceUrl}`, "_self");
+ };
+
+ return (
+
+
+
+
+ {name.replace("http://", "").replace("https://", "")}
+
+
+
+
+ );
+};
+
+export default Item;
diff --git a/packages/login/src/middleware.ts b/packages/login/src/middleware.ts
index 509a44ab34..1826c2e569 100644
--- a/packages/login/src/middleware.ts
+++ b/packages/login/src/middleware.ts
@@ -40,29 +40,26 @@ export function middleware(request: NextRequest) {
}
const isAuth = !!request.cookies.get("asc_auth_key")?.value;
+
const isOAuth = request.nextUrl.searchParams.get("type") === "oauth2";
-
- if (isOAuth) {
- const oauthClientId =
- request.nextUrl.searchParams.get("client_id") ??
- request.nextUrl.searchParams.get("clientId");
-
+ const oauthClientId =
+ request.nextUrl.searchParams.get("client_id") ??
+ request.nextUrl.searchParams.get("clientId");
+ if (isOAuth || oauthClientId) {
if (oauthClientId === "error")
return NextResponse.redirect(`${redirectUrl}/login/error`);
- if (isAuth) {
- if (request.nextUrl.pathname === "/consent") return;
-
+ if (isAuth && !request.nextUrl.pathname.includes("consent")) {
return NextResponse.redirect(
`${redirectUrl}/login/consent${request.nextUrl.search}`,
);
}
+ } else {
+ const url = request.nextUrl.clone();
+ url.pathname = "/";
+
+ if (isAuth && redirectUrl) return NextResponse.redirect(redirectUrl);
}
-
- const url = request.nextUrl.clone();
- url.pathname = "/";
-
- if (isAuth && redirectUrl) return NextResponse.redirect(redirectUrl);
}
// See "Matching Paths" below to learn more
diff --git a/packages/login/src/types/index.ts b/packages/login/src/types/index.ts
index b66bc2e63a..8a0660233f 100644
--- a/packages/login/src/types/index.ts
+++ b/packages/login/src/types/index.ts
@@ -33,7 +33,7 @@ import {
TThirdPartyProvider,
} from "@docspace/shared/api/settings/types";
import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types";
-import { INoAuthClientProps } from "@docspace/shared/utils/oauth/interfaces";
+import { IClientProps } from "@docspace/shared/utils/oauth/types";
import { RecaptchaType, ThemeKeys } from "@docspace/shared/enums";
export type TDataContext = {
@@ -88,7 +88,7 @@ export type LoginFormProps = {
reCaptchaType?: RecaptchaType;
cookieSettingsEnabled: boolean;
clientId?: string;
- client?: INoAuthClientProps;
+ client?: IClientProps;
};
export type ForgotPasswordModalDialogProps = {
diff --git a/packages/login/src/utils/actions.ts b/packages/login/src/utils/actions.ts
index 11a253c2c5..eb93d152d5 100644
--- a/packages/login/src/utils/actions.ts
+++ b/packages/login/src/utils/actions.ts
@@ -42,10 +42,7 @@ import {
TThirdPartyProvider,
TVersionBuild,
} from "@docspace/shared/api/settings/types";
-import {
- INoAuthClientProps,
- IScope,
-} from "@docspace/shared/utils/oauth/interfaces";
+import { IScope } from "@docspace/shared/utils/oauth/types";
import { transformToClientProps } from "@docspace/shared/utils/oauth";
export const checkIsAuthenticated = async () => {
@@ -182,27 +179,9 @@ export async function getScopeList() {
return scopes as IScope[];
}
-export async function getOAuthClient(clientId: string, isAuth = true) {
- if (!isAuth) {
- const [getOAuthClient] = createRequest(
- [`/clients/${clientId}/info`],
- [["", ""]],
- "GET",
- );
-
- const oauthClient = await fetch(getOAuthClient);
-
- console.log(oauthClient);
-
- if (!oauthClient.ok) return;
-
- const client = (await oauthClient.json()) as INoAuthClientProps;
-
- return client;
- }
-
+export async function getOAuthClient(clientId: string) {
const [getOAuthClient] = createRequest(
- [`/clients/${clientId}`],
+ [`/clients/${clientId}/public/info`],
[["", ""]],
"GET",
);
@@ -232,6 +211,30 @@ export async function getPortalCultures() {
return cultures.response as TPortalCultures;
}
+export async function gitAvailablePortals(data: {
+ email: string;
+ passwordHash: string;
+}) {
+ const [gitAvailablePortals] = createRequest(
+ [`/portal/signin`],
+ [["Content-Type", "application/json"]],
+ "POST",
+ JSON.stringify(data),
+ true,
+ );
+
+ console.log(gitAvailablePortals.url);
+
+ const response = await fetch(gitAvailablePortals);
+ if (!response.ok) return null;
+
+ const { response: portals } = await response.json();
+
+ console.log(portals);
+
+ // return config;
+}
+
export async function getConfig() {
const baseUrl = getBaseUrl();
const config = await (
diff --git a/packages/login/src/utils/index.ts b/packages/login/src/utils/index.ts
index 7b91cfa1dd..f54c037fdd 100644
--- a/packages/login/src/utils/index.ts
+++ b/packages/login/src/utils/index.ts
@@ -138,3 +138,7 @@ export const getEmailFromInvitation = (encodeString: Nullable) => {
return queryParams.email;
};
+
+export const generateOAuth2ReferenceURl = (clientId: string) => {
+ return `/login/consent?clientId=${clientId}`;
+};