Client:OAuth: refactoring

This commit is contained in:
Timofey Boyko 2024-06-26 15:19:13 +03:00
parent d7914ad024
commit ab24a40a0e
35 changed files with 205 additions and 162 deletions

View File

@ -1,7 +1,6 @@
import { IClientProps } from "@docspace/shared/utils/oauth/interfaces";
import { IClientProps } from "@docspace/shared/utils/oauth/types";
import { DeviceUnionType } from "SRC_DIR/Hooks/useViewEffect";
import { ViewAsType } from "SRC_DIR/store/OAuthStore";
export interface OAuthProps {

View File

@ -39,6 +39,7 @@ const OAuth = ({
const { t } = useTranslation(["OAuth"]);
const [isLoading, setIsLoading] = React.useState<boolean>(true);
const startLoadingRef = React.useRef<null | Date>(null);
const getData = React.useCallback(async () => {
@ -88,7 +89,7 @@ const OAuth = ({
{isLoading ? (
<OAuthLoader viewAs={viewAs} currentDeviceType={currentDeviceType} />
) : isEmptyClientList ? (
<OAuthEmptyScreen t={t} />
<OAuthEmptyScreen />
) : (
<List
clients={clientList}

View File

@ -89,6 +89,17 @@ const StyledInputGroup = styled.div`
gap: 0px;
}
.public_client {
margin-top: 4px;
display: flex;
align-items: center;
label {
position: relative;
}
}
.label {
height: 20px;
}

View File

@ -2,7 +2,7 @@ import {
IClientProps,
IClientReqDTO,
IScope,
} from "@docspace/shared/utils/oauth/interfaces";
} from "@docspace/shared/utils/oauth/types";
import { SettingsStore } from "@docspace/shared/store/SettingsStore";
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";

View File

@ -5,7 +5,9 @@ import { TTranslation } from "@docspace/shared/types";
import { HelpButton } from "@docspace/shared/components/help-button";
import { FieldContainer } from "@docspace/shared/components/field-container";
import { Checkbox } from "@docspace/shared/components/checkbox";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/interfaces";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/types";
import { ToggleButton } from "@docspace/shared/components/toggle-button";
import { Text } from "@docspace/shared/components/text";
import { StyledBlock, StyledInputBlock } from "../ClientForm.styled";
@ -22,8 +24,13 @@ interface BasicBlockProps {
logoValue: string;
descriptionValue: string;
allowPkce: boolean;
isPublic: boolean;
changeValue: (name: keyof IClientReqDTO, value: string | boolean) => void;
changeValue: (
name: keyof IClientReqDTO,
value: string | boolean,
remove?: boolean,
) => void;
isEdit: boolean;
errorFields: string[];
@ -73,6 +80,7 @@ const BasicBlock = ({
logoValue,
descriptionValue,
allowPkce,
isPublic,
changeValue,
isEdit,
@ -140,6 +148,8 @@ const BasicBlock = ({
<Trans t={t} i18nKey="AllowPKCEHelpButton" ns="OAuth" />
);
const publicClientHelpButtonText = "Help text";
const isNameRequiredError = requiredErrorFields.includes("name");
const isWebsiteRequiredError = requiredErrorFields.includes("website_url");
const isNameError = errorFields.includes("name");
@ -203,7 +213,7 @@ const BasicBlock = ({
/>
<InputGroup
label={t("AuthenticationMethod")}
name="website_url"
name="auth_method"
placeholder={t("EnterURL")}
value={websiteUrlValue}
error=""
@ -220,6 +230,25 @@ const BasicBlock = ({
<HelpButton tooltipContent={pkceHelpButtonText} />
</div>
</InputGroup>
<InputGroup
label="Client type"
name="public_client"
placeholder={t("EnterURL")}
value=""
error=""
onChange={() => {}}
>
<div className="public_client">
<ToggleButton
isChecked={isPublic}
onChange={(e) => {
changeValue("is_public", e.target.checked);
}}
/>
<Text>Public client</Text>
<HelpButton tooltipContent={publicClientHelpButtonText} />
</div>
</InputGroup>
</StyledInputBlock>
</StyledBlock>
);

View File

@ -1,7 +1,6 @@
import React from "react";
import { DeviceType } from "@docspace/shared/enums";
import { Button, ButtonSize } from "@docspace/shared/components/button";
import { StyledButtonContainer } from "../ClientForm.styled";
interface ButtonsBlockProps {

View File

@ -6,7 +6,7 @@ import { SelectorAddButton } from "@docspace/shared/components/selector-add-butt
import { SelectedItem } from "@docspace/shared/components/selected-item";
import { InputSize, InputType } from "@docspace/shared/components/text-input";
import { TTranslation } from "@docspace/shared/types";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/interfaces";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/types";
import ArrowIcon from "PUBLIC_DIR/images/arrow.right.react.svg";

View File

@ -1,7 +1,7 @@
import React from "react";
import { TTranslation } from "@docspace/shared/types";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/interfaces";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/types";
import { StyledBlock, StyledInputBlock } from "../ClientForm.styled";

View File

@ -4,7 +4,7 @@ import {
IClientReqDTO,
IFilteredScopes,
IScope,
} from "@docspace/shared/utils/oauth/interfaces";
} from "@docspace/shared/utils/oauth/types";
import {
filterScopeByGroup,
getScopeTKeyName,

View File

@ -1,7 +1,7 @@
import React from "react";
import { TTranslation } from "@docspace/shared/types";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/interfaces";
import { IClientReqDTO } from "@docspace/shared/utils/oauth/types";
import { StyledBlock, StyledInputBlock } from "../ClientForm.styled";

View File

@ -6,8 +6,7 @@ import { useTranslation } from "react-i18next";
import {
IClientProps,
IClientReqDTO,
INoAuthClientProps,
} from "@docspace/shared/utils/oauth/interfaces";
} from "@docspace/shared/utils/oauth/types";
import { AuthenticationMethod } from "@docspace/shared/enums";
import { toastr } from "@docspace/shared/components/toast";
import { TData } from "@docspace/shared/components/toast/Toast.type";
@ -54,9 +53,9 @@ const ClientForm = ({
const [isRequestRunning, setIsRequestRunning] =
React.useState<boolean>(false);
const [initialClient, setInitialClient] = React.useState<
IClientProps | INoAuthClientProps
>({} as IClientProps);
const [initialClient, setInitialClient] = React.useState<IClientProps>(
{} as IClientProps,
);
const [form, setForm] = React.useState<IClientReqDTO>({
name: "",
logo: "",
@ -70,6 +69,7 @@ const ClientForm = ({
terms_url: "",
policy_url: "",
is_public: false,
allow_pkce: false,
scopes: [],
@ -212,6 +212,7 @@ const ClientForm = ({
allow_pkce: item.authenticationMethods
? item.authenticationMethods.includes(AuthenticationMethod.none)
: false,
is_public: item.isPublic ?? false,
scopes: item.scopes ? [...item.scopes] : [],
});
@ -302,18 +303,20 @@ const ClientForm = ({
});
return (
isValid &&
form.name &&
form.logo &&
form.allowed_origins.length > 0 &&
(form.name !== initialClient.name ||
form.logo !== initialClient.logo ||
form.description !== initialClient.description ||
form.allowed_origins.length !== initialClient.allowedOrigins.length ||
form.allow_pkce !==
initialClient.authenticationMethods.includes(
AuthenticationMethod.none,
))
(isValid &&
form.name &&
form.logo &&
form.allowed_origins.length > 0 &&
(form.name !== initialClient.name ||
form.logo !== initialClient.logo ||
form.description !== initialClient.description ||
form.allowed_origins.length !==
initialClient.allowedOrigins.length ||
form.allow_pkce !==
initialClient.authenticationMethods.includes(
AuthenticationMethod.none,
))) ||
form.is_public !== initialClient.isPublic
);
}
@ -377,6 +380,7 @@ const ClientForm = ({
descriptionValue={form.description}
logoValue={form.logo}
allowPkce={form.allow_pkce}
isPublic={form.is_public}
changeValue={onChangeForm}
isEdit={isEdit}
errorFields={errorFields}

View File

@ -1,5 +0,0 @@
import { TTranslation } from "@docspace/shared/types";
export interface EmptyScreenProps {
t: TTranslation;
}

View File

@ -1,19 +1,21 @@
import { useTranslation } from "react-i18next";
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
import EmptyScreenOauthSvgUrl from "PUBLIC_DIR/images/empty_screen_oauth.svg?url";
import RegisterNewButton from "../RegisterNewButton";
import { EmptyScreenProps } from "./EmptyScreen.types";
const OAuthEmptyScreen = () => {
const { t } = useTranslation(["OAuth"]);
const OAuthEmptyScreen = ({ t }: EmptyScreenProps) => {
return (
<EmptyScreenContainer
imageSrc={EmptyScreenOauthSvgUrl}
imageAlt="Empty oauth list"
headerText={t("NoOAuthAppHeader")}
subheadingText={t("OAuthAppDescription")}
buttons={<RegisterNewButton t={t} />}
buttons={<RegisterNewButton />}
/>
);
};

View File

@ -3,7 +3,7 @@ import { inject, observer } from "mobx-react";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
import { IClientProps, IScope } from "@docspace/shared/utils/oauth/interfaces";
import { IClientProps, IScope } from "@docspace/shared/utils/oauth/types";
import ScopeList from "@docspace/shared/utils/oauth/ScopeList";
import getCorrectDate from "@docspace/shared/utils/getCorrectDate";
import { getCookie } from "@docspace/shared/utils/cookie";

View File

@ -59,7 +59,6 @@ export const OAuthRow = (props: RowProps) => {
return (
<Row
key={item.clientId}
// data={item}
contextOptions={contextOptions}
onRowClick={handleRowClick}
element={element}

View File

@ -1,5 +1,3 @@
import React from "react";
import { Text } from "@docspace/shared/components/text";
import { ToggleButton } from "@docspace/shared/components/toggle-button";

View File

@ -1,5 +1,5 @@
import { TTranslation } from "@docspace/shared/types";
import { IClientProps } from "@docspace/shared/utils/oauth/interfaces";
import { IClientProps } from "@docspace/shared/utils/oauth/types";
import { ContextMenuModel } from "@docspace/shared/components/context-menu";
import { ViewAsType } from "SRC_DIR/store/OAuthStore";

View File

@ -54,6 +54,7 @@ const Header = (props: HeaderProps) => {
containerRef={{ current: tableRef }}
columns={defaultColumns}
columnStorageName={columnStorageName}
tableStorageName={columnStorageName}
sectionWidth={sectionWidth}
showSettings={false}
useReactWindow

View File

@ -1,4 +1,4 @@
import { IClientProps } from "@docspace/shared/utils/oauth/interfaces";
import { IClientProps } from "@docspace/shared/utils/oauth/types";
import { TTranslation } from "@docspace/shared/types";
import { ContextMenuModel } from "@docspace/shared/components/context-menu";

View File

@ -1,4 +1,3 @@
import React from "react";
import styled from "styled-components";
import { Text } from "@docspace/shared/components/text";

View File

@ -1,4 +1,3 @@
import React from "react";
import styled from "styled-components";
import { Text } from "@docspace/shared/components/text";

View File

@ -14,7 +14,7 @@ import { TableViewProps } from "./TableView.types";
import { TableWrapper } from "./TableView.styled";
const TABLE_VERSION = "1";
const COLUMNS_SIZE = `oauthConfigColumnsSize_ver-${TABLE_VERSION}`;
const COLUMNS_NAME = `oauthConfigColumnsSize_ver-${TABLE_VERSION}`;
const elementResizeDetector = elementResizeDetectorMaker({
strategy: "scroll",
@ -99,7 +99,7 @@ const TableView = ({
};
}, [clickOutside, setSelection]);
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
const columnStorageName = `${COLUMNS_NAME}=${userId}`;
const fetchMoreFiles = React.useCallback(
async ({ startIndex }: { startIndex: number; stopIndex: number }) => {

View File

@ -1,7 +1,7 @@
import styled from "styled-components";
import { useTranslation } from "react-i18next";
import { IClientProps } from "@docspace/shared/utils/oauth/interfaces";
import { IClientProps } from "@docspace/shared/utils/oauth/types";
import { Text } from "@docspace/shared/components/text";
import { Consumer } from "@docspace/shared/utils/context";
@ -47,16 +47,12 @@ const List = ({ clients, viewAs, currentDeviceType }: ListProps) => {
fontSize="12px"
fontWeight={400}
lineHeight="16px"
title="OAuth description"
tag=""
as="p"
color=""
textAlign=""
title={t("OAuthAppDescription")}
className="description"
>
{t("OAuthAppDescription")}
</Text>
<RegisterNewButton t={t} currentDeviceType={currentDeviceType} />
<RegisterNewButton currentDeviceType={currentDeviceType} />
<Consumer>
{(context) =>
viewAs === "table" ? (

View File

@ -3,7 +3,7 @@ import { inject, observer } from "mobx-react";
import styled, { useTheme } from "styled-components";
import { useTranslation } from "react-i18next";
import { IClientProps } from "@docspace/shared/utils/oauth/interfaces";
import { IClientProps } from "@docspace/shared/utils/oauth/types";
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";

View File

@ -1,9 +1,5 @@
import { TTranslation } from "@docspace/shared/types";
//@ts-ignore
import { DeviceUnionType } from "SRC_DIR/Hooks/useViewEffect";
export interface RegisterNewButtonProps {
t: TTranslation;
currentDeviceType?: DeviceUnionType;
}

View File

@ -1,13 +1,13 @@
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Button, ButtonSize } from "@docspace/shared/components/button";
import { RegisterNewButtonProps } from "./RegisterNewButton.types";
const RegisterNewButton = ({
t,
currentDeviceType,
}: RegisterNewButtonProps) => {
const RegisterNewButton = ({ currentDeviceType }: RegisterNewButtonProps) => {
const { t } = useTranslation(["OAuth", "Common"]);
const navigate = useNavigate();
const onClick = () => {

View File

@ -16,7 +16,7 @@ import {
IClientProps,
IClientReqDTO,
IScope,
} from "@docspace/shared/utils/oauth/interfaces";
} from "@docspace/shared/utils/oauth/types";
import { toastr } from "@docspace/shared/components/toast";
import { AuthenticationMethod } from "@docspace/shared/enums";
import { TData } from "@docspace/shared/components/toast/Toast.type";
@ -202,7 +202,7 @@ class OAuthStore implements OAuthStoreProps {
this.clientSecret = value;
};
setSelection = (clientId: string) => {
setSelection = (clientId?: string) => {
if (!clientId) {
this.selection = [];
} else if (this.selection.includes(clientId)) {
@ -229,7 +229,7 @@ class OAuthStore implements OAuthStoreProps {
this.clientsIsLoading = value;
};
setActiveClient = (clientId: string) => {
setActiveClient = (clientId?: string) => {
if (!clientId) {
this.activeClients = [];
} else if (this.activeClients.includes(clientId)) {
@ -257,7 +257,7 @@ class OAuthStore implements OAuthStoreProps {
this.clients = [...clientList.content];
this.selection = [];
this.currentPage = clientList.page;
this.nextPage = clientList.next || null;
this.nextPage = clientList.next;
if (clientList.next) {
this.itemCount = clientList.content.length + 2;
@ -272,19 +272,6 @@ class OAuthStore implements OAuthStoreProps {
}
};
fetchConsents = async () => {
try {
const consentList: IClientProps[] = await getConsentList();
runInAction(() => {
this.consents = [...consentList];
});
} catch (e) {
const err = e as TData;
toastr.error(err);
}
};
fetchNextClients = async (startIndex: number) => {
if (this.clientsIsLoading) return;
@ -312,6 +299,19 @@ class OAuthStore implements OAuthStoreProps {
this.setClientsIsLoading(false);
};
fetchConsents = async () => {
try {
const consentList: IClientProps[] = await getConsentList();
runInAction(() => {
this.consents = [...consentList];
});
} catch (e) {
const err = e as TData;
toastr.error(err);
}
};
saveClient = async (client: IClientReqDTO) => {
try {
const newClient = await addClient(client);
@ -343,6 +343,7 @@ class OAuthStore implements OAuthStoreProps {
newClient.allowedOrigins = client.allowed_origins;
newClient.logo = client.logo;
newClient.description = client.description;
newClient.isPublic = client.is_public;
if (
client.allow_pkce &&

View File

@ -1,5 +1,5 @@
{
"date": "2024624_152825",
"date": "2024626_125743",
"checksums": {
"api.js": "0efbae3383bf6c6b6f26d573eee164d2",
"api.poly.js": "2a2ac2c0e4a7007b61d2d1ff7b00a22e",

View File

@ -142,3 +142,17 @@ export const checkDomain = async (domain: string) => {
});
return res;
};
export const getAvailablePortals = async (data: {
Email: string;
PasswordHash: string;
}) => {
const res = (await request({
baseURL,
method: "post",
url: `/portal/signin?Email=${data.Email}&PasswordHash=${data.PasswordHash}`,
data,
})) as { tenants: { portalLink: string; portalName: string }[] };
return res.tenants;
};

View File

@ -8,26 +8,11 @@ import {
IClientListProps,
IClientListDTO,
IScope,
INoAuthClientProps,
IClientReqDTO,
IGetConsentList,
} from "../../utils/oauth/interfaces";
export const getClient = async (
clientId: string,
isAuth: boolean = true,
): Promise<IClientProps | INoAuthClientProps> => {
if (!isAuth) {
const client = (await request({
method: "get",
url: `/clients/${clientId}/info`,
})) as INoAuthClientProps;
return {
...client,
};
}
} from "../../utils/oauth/types";
export const getClient = async (clientId: string): Promise<IClientProps> => {
const client = (await request({
method: "get",
url: `/clients/${clientId}`,
@ -45,7 +30,7 @@ export const getClientList = async (
url: `/clients?page=${page}&limit=${limit}`,
})) as IClientListDTO;
const clients: IClientListProps = { ...data, content: [] as IClientProps[] };
const clients: IClientListProps = { ...data, content: [] };
data.data.forEach((item) => {
const client = transformToClientProps(item);
@ -123,6 +108,36 @@ export const getScopeList = async (): Promise<IScope[]> => {
return scopeList;
};
export const getConsentList = async (): Promise<IClientProps[]> => {
const clients = (await request({
method: "get",
url: "/clients/consents",
})) as IGetConsentList[];
const consents: IClientProps[] = [];
clients.forEach(({ client, invalidated, modified_at }: IGetConsentList) => {
const consentClient: IClientResDTO = {
...client,
client_secret: "",
logout_redirect_uri: "",
};
const cl = transformToClientProps(consentClient);
if (!invalidated) consents.push({ ...cl, modifiedOn: modified_at });
});
return consents;
};
export const revokeUserClient = async (clientId: string): Promise<void> => {
await request({
method: "delete",
url: `/clients/${clientId}/revoke`,
});
};
export const onOAuthLogin = (clientId: string) => {
const formData = new FormData();
@ -182,33 +197,3 @@ export const onOAuthCancel = (clientId: string, clientState: string) => {
},
});
};
export const getConsentList = async (): Promise<IClientProps[]> => {
const clients = (await request({
method: "get",
url: "/clients/consents",
})) as IGetConsentList[];
const consents: IClientProps[] = [];
clients.forEach(({ client, invalidated, modified_at }: IGetConsentList) => {
const consentClient: IClientResDTO = {
...client,
client_secret: "",
logout_redirect_uri: "",
};
const cl = transformToClientProps(consentClient);
if (!invalidated) consents.push({ ...cl, modifiedOn: modified_at });
});
return consents;
};
export const revokeUserClient = async (clientId: string): Promise<void> => {
await request({
method: "delete",
url: `/clients/${clientId}/revoke`,
});
};

View File

@ -992,7 +992,7 @@ const Dark: TTheme = {
border: "none",
},
},
fieldContainer: {
horizontal: {
margin: "0 0 16px 0",

View File

@ -39,10 +39,10 @@ export const getBaseUrl = () => {
return baseURL;
};
export const getAPIUrl = () => {
export const getAPIUrl = (apiSystem?: boolean) => {
const baseUrl = process.env.API_HOST?.trim() ?? getBaseUrl();
const baseAPIUrl = `${baseUrl}/${API_PREFIX}`;
const baseAPIUrl = `${baseUrl}/${!apiSystem ? API_PREFIX : "apisystem"}`;
return baseAPIUrl;
};
@ -52,11 +52,12 @@ export const createRequest = (
newHeaders: [string, string][],
method: string,
body?: string,
apiSystem?: boolean,
) => {
const hdrs = new Headers(headers());
const cookieStore = cookies();
const apiURL = getAPIUrl();
const apiURL = getAPIUrl(apiSystem);
newHeaders.forEach((hdr) => {
if (hdr[0]) hdrs.set(hdr[0], hdr[1]);

View File

@ -5,7 +5,7 @@ import { Base } from "../../themes";
import { ScopeType } from "../../enums";
import { TTranslation } from "../../types";
import { IFilteredScopes, IScope } from "./interfaces";
import { IFilteredScopes, IScope } from "./types";
import { filterScopeByGroup } from ".";
const StyledScopeList = styled.div`

View File

@ -9,7 +9,7 @@ import {
IClientProps,
IScope,
IFilteredScopes,
} from "./interfaces";
} from "./types";
export const transformToClientProps = (
clientDto: IClientResDTO,
@ -37,6 +37,7 @@ export const transformToClientProps = (
allowed_origins,
creator_avatar,
creator_display_name,
is_public,
} = clientDto;
const client: IClientProps = {
@ -62,6 +63,7 @@ export const transformToClientProps = (
allowedOrigins: allowed_origins,
creatorAvatar: creator_avatar,
creatorDisplayName: creator_display_name,
isPublic: is_public,
};
return client;
@ -82,6 +84,7 @@ export const transformToClientReqDTO = (
scopes,
websiteUrl,
allowedOrigins,
isPublic,
} = clientProps;
const client: IClientReqDTO = {
@ -92,7 +95,7 @@ export const transformToClientReqDTO = (
logout_redirect_uri,
terms_url,
policy_url,
is_public: isPublic,
scopes,
allow_pkce: authenticationMethods.includes(AuthenticationMethod.none),
website_url: websiteUrl,

View File

@ -1,3 +1,4 @@
import { Nullable } from "../../types";
import { AuthenticationMethod, ScopeGroup, ScopeType } from "../../enums";
export interface IScope {
@ -61,12 +62,13 @@ export interface IClientProps {
scopes: string[];
websiteUrl: string;
allowedOrigins: string[];
createdOn?: Date;
modifiedOn?: Date;
createdBy?: string;
modifiedBy?: string;
createdOn: Date;
modifiedOn: Date;
createdBy: string;
modifiedBy: string;
creatorAvatar?: string;
creatorDisplayName?: string;
isPublic: boolean;
}
export interface IClientReqDTO {
@ -81,54 +83,61 @@ export interface IClientReqDTO {
scopes: string[];
website_url: string;
allowed_origins: string[];
is_public: boolean;
}
export interface IClientResDTO {
name: string;
allowed_origins: string[];
authentication_methods: AuthenticationMethod[];
client_id: string;
client_secret: string;
description: string;
logo: string;
created_by: string;
created_on: Date;
redirect_uris: string[];
terms_url: string;
policy_url: string;
creator_avatar?: string;
creator_display_name?: string;
description: string;
enabled: boolean;
invalidated: boolean;
is_public: boolean;
logo: string;
logout_redirect_uri: string;
authentication_methods: AuthenticationMethod[];
modified_by: string;
modified_on: Date;
name: string;
policy_url: string;
redirect_uris: string[];
scopes: string[];
enabled: boolean;
terms_url: string;
tenant: number;
invalidated: boolean;
website_url: string;
allowed_origins: string[];
created_on?: Date;
modified_on?: Date;
created_by?: string;
modified_by?: string;
creator_avatar?: string;
creator_display_name?: string;
website_url: string;
}
export interface IClientListProps {
content: IClientProps[];
page: number;
next?: number;
previous?: number;
limit: number;
next: Nullable<number>;
previous: Nullable<number>;
}
export interface IClientListDTO {
data: IClientResDTO[];
page: number;
next?: number;
previous?: number;
limit: number;
next: Nullable<number>;
previous: Nullable<number>;
}
export interface ISubmitReqDTO {
@ -159,6 +168,7 @@ export type TConsentClient = {
redirect_uris: string[];
scopes: string[];
allowed_origins: string[];
is_public: boolean;
};
export interface IGetConsentList {
@ -168,4 +178,5 @@ export interface IGetConsentList {
principal_name: string;
registered_client_id: string;
scopes: string;
is_public: boolean;
}