From 5e70e6fee546dc8372b4f536feae1e9e10f9bc61 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Thu, 30 May 2024 10:07:09 +0300 Subject: [PATCH] Login: fix oauth pages --- .../OAuth/sub-components/DeleteDialog.tsx | 4 +- .../OAuth/sub-components/DisableDialog.tsx | 5 +- .../OAuth/sub-components/InfoDialog.tsx | 28 +- .../OAuth/sub-components/PreviewDialog.tsx | 24 +- .../OAuth/sub-components/ResetDialog.tsx | 5 +- packages/doceditor/package.json | 2 +- packages/login/package.json | 2 +- packages/login/public/locales/en/Consent.json | 2 +- .../login/src/app/(root)/consent/page.tsx | 52 ++++ packages/login/src/app/(root)/layout.tsx | 1 + packages/login/src/app/(root)/page.tsx | 23 +- .../components/sub-components/Consent.tsx | 228 ---------------- packages/login/src/components/Consent.tsx | 257 ++++++++++++++++++ .../ConsentInfo.tsx} | 36 +-- .../login/src/components/LoginForm/index.tsx | 17 +- packages/login/src/middleware.ts | 20 +- packages/login/src/types/index.ts | 3 + packages/login/src/utils/actions.ts | 74 ++++- packages/runtime.json | 2 +- packages/shared/api/oauth/index.ts | 9 +- .../components/docspace-logo/DocspaceLogo.tsx | 2 + packages/shared/themes/dark.ts | 2 +- packages/shared/utils/axiosClient.ts | 3 + packages/shared/utils/next-ssr-helper.ts | 2 + 24 files changed, 498 insertions(+), 305 deletions(-) create mode 100644 packages/login/src/app/(root)/consent/page.tsx delete mode 100644 packages/login/src/client/components/sub-components/Consent.tsx create mode 100644 packages/login/src/components/Consent.tsx rename packages/login/src/{client/components/sub-components/oauth-client-info.tsx => components/ConsentInfo.tsx} (79%) diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DeleteDialog.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DeleteDialog.tsx index c14d431268..90289fc6f0 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DeleteDialog.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DeleteDialog.tsx @@ -8,7 +8,6 @@ import { Button, ButtonSize } from "@docspace/shared/components/button"; import { toastr } from "@docspace/shared/components/toast"; import { TData } from "@docspace/shared/components/toast/Toast.type"; -// @ts-ignore import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; interface DeleteClientDialogProps { @@ -55,7 +54,7 @@ const DeleteClientDialog = (props: DeleteClientDialogProps) => { label={t("Common:OkButton")} size={ButtonSize.normal} scale - primary={true} + primary isLoading={isRequestRunning} onClick={onDisableClick} /> @@ -88,6 +87,7 @@ export default inject(({ oauthStore }: { oauthStore: OAuthStoreProps }) => { }; const onDisable = async () => { + if (!bufferSelection) return; setActiveClient(bufferSelection.clientId); await deleteClient([bufferSelection.clientId]); setActiveClient(""); diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DisableDialog.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DisableDialog.tsx index bc314efd1d..604c7256b4 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DisableDialog.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/DisableDialog.tsx @@ -8,7 +8,6 @@ import { Button, ButtonSize } from "@docspace/shared/components/button"; import { toastr } from "@docspace/shared/components/toast"; import { TData } from "@docspace/shared/components/toast/Toast.type"; -// @ts-ignore import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; interface DisableClientDialogProps { @@ -55,7 +54,7 @@ const DisableClientDialog = (props: DisableClientDialogProps) => { label={t("Common:OkButton")} size={ButtonSize.normal} scale - primary={true} + primary isLoading={isRequestRunning} onClick={onDisableClick} /> @@ -88,6 +87,8 @@ export default inject(({ oauthStore }: { oauthStore: OAuthStoreProps }) => { }; const onDisable = async () => { + if (!bufferSelection) return; + setActiveClient(bufferSelection.clientId); await changeClientStatus(bufferSelection.clientId, false); setActiveClient(""); diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/InfoDialog.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/InfoDialog.tsx index ed7e3bf7ab..586a15595f 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/InfoDialog.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/InfoDialog.tsx @@ -5,11 +5,8 @@ import { useTranslation } from "react-i18next"; import { IClientProps, IScope } from "@docspace/shared/utils/oauth/interfaces"; import ScopeList from "@docspace/shared/utils/oauth/ScopeList"; - import getCorrectDate from "@docspace/shared/utils/getCorrectDate"; - import { getCookie } from "@docspace/shared/utils/cookie"; - import { ModalDialog } from "@docspace/shared/components/modal-dialog"; import { ModalDialogType } from "@docspace/shared/components/modal-dialog/ModalDialog.enums"; import { Text } from "@docspace/shared/components/text"; @@ -17,23 +14,18 @@ import { ContextMenuButton, ContextMenuButtonDisplayType, } from "@docspace/shared/components/context-menu-button"; - -// @ts-ignore -import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; import { Avatar, AvatarRole, AvatarSize, } from "@docspace/shared/components/avatar"; -import { - LinkTarget, - LinkType, -} from "@docspace/shared/components/link/Link.enums"; -import { Link } from "@docspace/shared/components/link"; +import { Link, LinkTarget, LinkType } from "@docspace/shared/components/link"; import { Base } from "@docspace/shared/themes"; import { TTranslation } from "@docspace/shared/types"; import { ContextMenuModel } from "@docspace/shared/components/context-menu"; +import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; + const StyledContainer = styled.div<{ showDescription: boolean; withShowText: boolean; @@ -289,7 +281,7 @@ const InfoDialog = ({ fontWeight="600" isHovered onClick={() => setShowDescription((val) => !val)} - type={"action"} + type={LinkType.action} > {showDescription ? "Hide" : "Show more"} @@ -314,8 +306,8 @@ const InfoDialog = ({ fontWeight="600" isHovered href={client?.websiteUrl} - type={"action"} - target={"_blank"} + type={LinkType.action} + target={LinkTarget.blank} > {client?.websiteUrl} @@ -385,8 +377,8 @@ const InfoDialog = ({ fontWeight="600" isHovered href={client?.policyUrl} - type={"action"} - target={"_blank"} + type={LinkType.action} + target={LinkTarget.blank} > {t("PrivacyPolicy")} @@ -398,8 +390,8 @@ const InfoDialog = ({ fontWeight="600" isHovered href={client?.termsUrl} - type={"action"} - target={"_blank"} + type={LinkType.action} + target={LinkTarget.blank} > {t("Terms of Service")} diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/PreviewDialog.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/PreviewDialog.tsx index 8548f38350..fed02eb059 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/PreviewDialog.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/PreviewDialog.tsx @@ -1,27 +1,25 @@ import React from "react"; import { inject, observer } from "mobx-react"; -import styled from "styled-components"; +import styled, { useTheme } from "styled-components"; import { useTranslation } from "react-i18next"; import { IClientProps } from "@docspace/shared/utils/oauth/interfaces"; - import { ModalDialog } from "@docspace/shared/components/modal-dialog"; import { ModalDialogType } from "@docspace/shared/components/modal-dialog/ModalDialog.enums"; import { SocialButton } from "@docspace/shared/components/social-button"; import { Text } from "@docspace/shared/components/text"; import { Textarea } from "@docspace/shared/components/textarea"; - -import OnlyofficeLight from "PUBLIC_DIR/images/onlyoffice.light.react.svg"; -import OnlyofficeDark from "PUBLIC_DIR/images/onlyoffice.dark.react.svg"; - -// @ts-ignore -import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; import { Button, ButtonSize } from "@docspace/shared/components/button"; import { Base } from "@docspace/shared/themes"; import { generatePKCEPair } from "@docspace/shared/utils/oauth"; import { AuthenticationMethod } from "@docspace/shared/enums"; import { SettingsStore } from "@docspace/shared/store/SettingsStore"; +import OnlyofficeLight from "PUBLIC_DIR/images/onlyoffice.light.react.svg"; +import OnlyofficeDark from "PUBLIC_DIR/images/onlyoffice.dark.react.svg"; + +import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; + const StyledContainer = styled.div` width: 100%; height: 100%; @@ -152,17 +150,15 @@ interface PreviewDialogProps { setPreviewDialogVisible?: (value: boolean) => void; client?: IClientProps; - - theme?: any; } const PreviewDialog = ({ visible, setPreviewDialogVisible, client, - theme, }: PreviewDialogProps) => { const { t } = useTranslation(["OAuth", "Common", "Webhooks"]); + const theme = useTheme(); const [codeVerifier, setCodeVerifier] = React.useState(""); const [codeChallenge, setCodeChallenge] = React.useState(""); @@ -181,16 +177,16 @@ const PreviewDialog = ({ const encodingScopes = encodeURI(scopesString || ""); const getData = React.useCallback(() => { - const { verifier, challenge, state } = generatePKCEPair(); + const { verifier, challenge, state: s } = generatePKCEPair(); setCodeVerifier(verifier); setCodeChallenge(challenge); - setState(state); + setState(s); }, []); React.useEffect(() => { getData(); - }, []); + }, [getData]); const getLink = () => { return `${ diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/ResetDialog.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/ResetDialog.tsx index 537298bb19..5862c28e92 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/ResetDialog.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/ResetDialog.tsx @@ -7,11 +7,10 @@ import { ModalDialog } from "@docspace/shared/components/modal-dialog"; import { ModalDialogType } from "@docspace/shared/components/modal-dialog/ModalDialog.enums"; import { Button, ButtonSize } from "@docspace/shared/components/button"; import { toastr } from "@docspace/shared/components/toast"; - -// @ts-ignore -import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; import { TData } from "@docspace/shared/components/toast/Toast.type"; +import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; + interface ResetDialogProps { isVisible?: boolean; onClose?: () => void; diff --git a/packages/doceditor/package.json b/packages/doceditor/package.json index 4f3a9f0ebb..d3327a5c30 100644 --- a/packages/doceditor/package.json +++ b/packages/doceditor/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@svgr/webpack": "^8.1.0", "@types/node": "^20", - "@types/react": "^18", + "@types/react": "^18.2.53", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.0.4", diff --git a/packages/login/package.json b/packages/login/package.json index eaaa08c145..35088eed93 100644 --- a/packages/login/package.json +++ b/packages/login/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@svgr/webpack": "^8.1.0", "@types/node": "^20", - "@types/react": "^18", + "@types/react": "^18.2.53", "@types/react-dom": "^18", "@types/react-google-recaptcha": "^2.1.9", "babel-plugin-styled-components": "^2.1.4", diff --git a/packages/login/public/locales/en/Consent.json b/packages/login/public/locales/en/Consent.json index cb3531f1f4..f0d6cade56 100644 --- a/packages/login/public/locales/en/Consent.json +++ b/packages/login/public/locales/en/Consent.json @@ -2,7 +2,7 @@ "Consent": "Consent", "ConsentSubHeader": "{{name}} would like the ability to access the following data in your DocSpace account:", "ConsentDescription": "Data shared with {{displayName}} will be governed by {{nameApp}} <6>privacy policy and <6>terms of service. You can revoke this consent at any time in your DocSpace account settings.", - "ToContinue": "to continue to", + "ToContinue": "To continue to", "SignedInAs": "Signed in as", "NotYou": "Not you?" } diff --git a/packages/login/src/app/(root)/consent/page.tsx b/packages/login/src/app/(root)/consent/page.tsx new file mode 100644 index 0000000000..cf48345a32 --- /dev/null +++ b/packages/login/src/app/(root)/consent/page.tsx @@ -0,0 +1,52 @@ +// (c) Copyright Ascensio System SIA 2009-2024 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// 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 Consent from "@/components/Consent"; +import { getOAuthClient, getScopeList, getUser } from "@/utils/actions"; + +async function Page({ + searchParams, +}: { + searchParams: { [key: string]: string }; +}) { + const clientId = searchParams.clientId ?? searchParams.client_id; + const [client, scopes, user] = await Promise.all([ + getOAuthClient(clientId, true), + getScopeList(), + getUser(), + ]); + + if (!client || (client && !("clientId" in client)) || !scopes || !user) + return ""; + + return ( + + ); +} + +export default Page; diff --git a/packages/login/src/app/(root)/layout.tsx b/packages/login/src/app/(root)/layout.tsx index 34cc4cbb0f..f20ff75295 100644 --- a/packages/login/src/app/(root)/layout.tsx +++ b/packages/login/src/app/(root)/layout.tsx @@ -24,6 +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 React from "react"; import { cookies } from "next/headers"; import { SYSTEM_THEME_KEY } from "@docspace/shared/constants"; diff --git a/packages/login/src/app/(root)/page.tsx b/packages/login/src/app/(root)/page.tsx index 5fd06d7525..d369ea42ee 100644 --- a/packages/login/src/app/(root)/page.tsx +++ b/packages/login/src/app/(root)/page.tsx @@ -24,15 +24,26 @@ // 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 { getSettings } from "@/utils/actions"; +import { INoAuthClientProps } from "@docspace/shared/utils/oauth/interfaces"; + +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"; -async function Page() { - const settings = await getSettings(); +async function Page({ + searchParams, +}: { + searchParams: { [key: string]: string }; +}) { + const clientId = searchParams.clientId; + + const [settings, client] = await Promise.all([ + getSettings(), + clientId ? getOAuthClient(clientId, false) : undefined, + ]); return ( @@ -41,10 +52,12 @@ async function Page() { - + {!clientId && } {settings.enableAdmMess && } - {settings.enabledJoin && ( + {settings.enabledJoin && !clientId && ( props.theme.oauth.infoDialog.separatorColor}; - - .block { - height: 40px; - - display: flex; - align-items: center; - gap: 8px; - } - } -`; - -StyledFormWrapper.defaultProps = { theme: Base }; - -interface IConsentProps { - oauth: IOAuthState; - theme: IUserTheme; - hashSettings: null | PasswordHashType; - setHashSettings: (hashSettings: PasswordHashType | null) => void; - setIsConsentScreen: (value: boolean) => void; -} - -const Consent = ({ - oauth, - theme, - setIsConsentScreen, - hashSettings, - setHashSettings, -}: IConsentProps) => { - const navigate = useNavigate(); - const location = useLocation(); - - const { t } = useTranslation(["Consent", "Common"]); - - const onAllowClick = async () => { - const clientId = oauth.clientId; - - let clientState = ""; - const scope = oauth.client.scopes; - - await api.oauth.onOAuthLogin(clientId); - - const cookie = document.cookie.split(";"); - - cookie.forEach((c) => { - if (c.includes("client_state")) - clientState = c.replace("client_state=", "").trim(); - }); - - deleteCookie("client_state"); - - await api.oauth.onOAuthSubmit(clientId, clientState, scope); - }; - - const onDenyClick = async () => { - const clientId = oauth.clientId; - - let clientState = ""; - - await api.oauth.onOAuthLogin(clientId); - - const cookie = document.cookie.split(";"); - - cookie.forEach((c) => { - if (c.includes("client_state")) - clientState = c.replace("client_state=", "").trim(); - }); - - deleteCookie("client_state"); - - await api.oauth.onOAuthCancel(clientId, clientState); - }; - - const onChangeUserClick = async () => { - await api.user.logout(); - if (!hashSettings) { - const portalSettings = await api.settings.getSettings(); - - setHashSettings(portalSettings.passwordHash); - } - - setIsConsentScreen(false); - navigate(`/login/${location.search}`); - }; - - return ( - - - - - -
-
-
- - - Data shared with {{ displayName: oauth.self?.displayName }} will be - governed by {{ nameApp: oauth.client.name }} - - privacy policy - - and - - terms of service - - . You can revoke this consent at any time in your DocSpace account - settings. - - -
-
-
- -
- - {t("SignedInAs")} {oauth.self?.email} - - - {t("NotYou")} - -
-
-
-
- ); -}; - -export default Consent; diff --git a/packages/login/src/components/Consent.tsx b/packages/login/src/components/Consent.tsx new file mode 100644 index 0000000000..c2c7aff5cd --- /dev/null +++ b/packages/login/src/components/Consent.tsx @@ -0,0 +1,257 @@ +/* eslint-disable @next/next/no-img-element */ +// (c) Copyright Ascensio System SIA 2009-2024 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// 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 + +"use client"; + +import React from "react"; +import styled from "styled-components"; +import { useTranslation, Trans } from "react-i18next"; + +import ScopeList from "@docspace/shared/utils/oauth/ScopeList"; +import { Button, ButtonSize } from "@docspace/shared/components/button"; +import { Text } from "@docspace/shared/components/text"; +import { Link, LinkTarget, LinkType } from "@docspace/shared/components/link"; +import { + Avatar, + AvatarRole, + AvatarSize, +} from "@docspace/shared/components/avatar"; +import { deleteCookie } from "@docspace/shared/utils/cookie"; +import { IClientProps, IScope } from "@docspace/shared/utils/oauth/interfaces"; +import { TUser } from "@docspace/shared/api/people/types"; +import api from "@docspace/shared/api"; + +import OAuthClientInfo from "./ConsentInfo"; +import { useRouter } from "next/navigation"; + +const StyledButtonContainer = styled.div` + margin-top: 32px; + margin-bottom: 16px; + + width: 100%; + display: flex; + flex-direction: row; + gap: 8px; +`; + +const StyledDescriptionContainer = styled.div` + width: 100%; + + margin-bottom: 16px; + + p { + width: 100%; + } +`; + +const StyledUserContainer = styled.div` + width: 100%; + + padding-top: 16px; + + border-top: 1px solid + ${(props) => props.theme.oauth.infoDialog.separatorColor}; + + .block { + height: 40px; + + display: flex; + align-items: center; + gap: 8px; + } +`; + +interface IConsentProps { + client: IClientProps; + scopes: IScope[]; + user: TUser; +} + +const Consent = ({ client, scopes, user }: IConsentProps) => { + const { t } = useTranslation(["Consent", "Common"]); + const router = useRouter(); + + const [isAllowRunning, setIsAllowRunning] = React.useState(false); + const [isDenyRunning, setIsDenyRunning] = React.useState(false); + + const onAllowClick = async () => { + if (!("clientId" in client)) return; + + if (isAllowRunning || isDenyRunning) return; + + setIsAllowRunning(true); + + const clientId = client.clientId; + + let clientState = ""; + + console.log(clientState); + const scope = client.scopes; + + const cookie = document.cookie.split(";"); + + cookie.forEach((c) => { + if (c.includes("client_state")) + clientState = c.replace("client_state=", "").trim(); + }); + + deleteCookie("client_state"); + + console.log(clientState); + + await api.oauth.onOAuthSubmit(clientId, clientState, scope); + + setIsAllowRunning(false); + }; + + const onDenyClick = async () => { + if (!("clientId" in client)) return; + + if (isAllowRunning || isDenyRunning) return; + + setIsDenyRunning(true); + + const clientId = client.clientId; + + let clientState = ""; + + // await api.oauth.onOAuthLogin(clientId); + + const cookie = document.cookie.split(";"); + + cookie.forEach((c) => { + if (c.includes("client_state")) + clientState = c.replace("client_state=", "").trim(); + }); + + deleteCookie("client_state"); + + await api.oauth.onOAuthCancel(clientId, clientState); + + setIsDenyRunning(false); + }; + + const onChangeUserClick = async () => { + await api.user.logout(); + + router.push(`/?clientId=${client.clientId}`); + }; + + return ( + <> + + + + + +