Merge pull request #592 from ONLYOFFICE/bugfix/oauth2-scope

Bugfix/oauth2 scope
This commit is contained in:
Alexey Bannov 2024-08-20 10:32:37 +03:00 committed by GitHub
commit a77fd92d09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 137 additions and 25 deletions

View File

@ -242,7 +242,6 @@ const StyledProperties = styled.div`
gap: 4px;
.property-tag {
background: red;
max-width: 195px;
margin: 0;
background: ${(props) => props.theme.infoPanel.details.tagBackground};

View File

@ -56,6 +56,8 @@ const ScopesBlock = ({
) => {
const isChecked = checkedScopes.includes(name);
const isWrite = type === "write";
if (!isChecked) {
setFilteredScopes((val) => {
val[group].isChecked = true;
@ -88,6 +90,8 @@ const ScopesBlock = ({
setCheckedScopes((val) => val.filter((v) => v !== name));
}
if (isWrite) onAddScope("scopes", name.replace("write", "read"));
onAddScope("scopes", name);
};

View File

@ -25,6 +25,7 @@ import { TTranslation } from "@docspace/shared/types";
import { ContextMenuModel } from "@docspace/shared/components/context-menu";
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
import { Tag } from "@docspace/shared/components/tag";
const StyledContainer = styled.div<{
showDescription: boolean;
@ -118,6 +119,23 @@ const StyledContainer = styled.div<{
background: ${(props) => props.theme.oauth.infoDialog.separatorColor};
}
}
.property-tag_list {
display: flex;
flex-wrap: wrap;
gap: 4px;
.property-tag {
max-width: 195px;
margin: 0;
background: ${(props) => props.theme.infoPanel.details.tagBackground};
p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
`;
StyledContainer.defaultProps = { theme: Base };
@ -288,7 +306,6 @@ const InfoDialog = ({
)}
</>
)}
<Text
className="block-header"
fontSize="14px"
@ -299,7 +316,6 @@ const InfoDialog = ({
>
{t("Common:Website")}
</Text>
<Link
fontSize="13px"
lineHeight="15px"
@ -311,7 +327,21 @@ const InfoDialog = ({
>
{client?.websiteUrl}
</Link>
<Text
className="block-header"
fontSize="14px"
lineHeight="16px"
fontWeight="600"
noSelect
truncate
>
{t("Scopes")}
</Text>{" "}
<ScopeList
selectedScopes={client?.scopes || []}
scopes={scopeList || []}
t={t}
/>
<Text
className="block-header"
fontSize="14px"
@ -322,11 +352,16 @@ const InfoDialog = ({
>
{t("Access")}
</Text>
<ScopeList
selectedScopes={client?.scopes || []}
scopes={scopeList || []}
t={t}
<div className="property-tag_list">
{client?.scopes.map((scope) => (
<Tag
key={scope}
tag={scope}
className="property-tag"
label={scope}
/>
))}
</div>
{isProfile && (
<>
<Text
@ -351,7 +386,6 @@ const InfoDialog = ({
</Text>
</>
)}
<Text
className="block-header"
fontSize="14px"
@ -362,7 +396,6 @@ const InfoDialog = ({
>
{t("SupportAndLegalInfo")}
</Text>
<Text
className="privacy-block"
fontSize="13px"

View File

@ -27,6 +27,8 @@ const StyledImage = styled.img`
height: 32px;
border-radius: 3px;
object-fit: cover;
`;
interface NameCellProps {

View File

@ -21,9 +21,11 @@ import {
import { UserStore } from "@docspace/shared/store/UserStore";
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
import { introspectDeveloperToken } from "@docspace/shared/api/oauth";
import { FieldContainer } from "@docspace/shared/components/field-container";
const StyledContainer = styled.div`
p {
.warning-text {
margin-bottom: 16px;
}
`;
@ -42,11 +44,15 @@ const GenerateDeveloperTokenDialog = ({
// const {} = useTranslation(["OAuth", "Common"]);
const [token, setToken] = React.useState("");
const [isValidToken, setIsValidToken] = React.useState(false);
const [tokenError, setTokenError] = React.useState("");
const [requestRunning, setRequestRunning] = React.useState(false);
const timerRef = React.useRef<null | NodeJS.Timeout>(null);
const onRevoke = async () => {
if (!token || !client || requestRunning) return;
if (!token || !isValidToken || !client || requestRunning) return;
try {
const { clientId, clientSecret } = client;
@ -66,9 +72,42 @@ const GenerateDeveloperTokenDialog = ({
}
};
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const onChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
if (timerRef.current) {
clearTimeout(timerRef.current);
}
if (!value) {
setIsValidToken(false);
setTokenError("");
setToken("");
return;
}
timerRef.current = setTimeout(async () => {
try {
const data = await introspectDeveloperToken(value);
if (!data) return;
const { active, client_id: clientId } = data;
if (active && clientId === client?.clientId) {
setIsValidToken(true);
setTokenError("");
return;
}
setIsValidToken(false);
setTokenError("Invalid token");
} catch (err) {
setIsValidToken(false);
setTokenError("Invalid token");
}
}, 200);
setToken(value);
};
@ -89,7 +128,12 @@ const GenerateDeveloperTokenDialog = ({
<ModalDialog.Header>Revoke developer token</ModalDialog.Header>
<ModalDialog.Body>
<StyledContainer>
<Text>Warning text</Text>
<Text className="warning-text">Warning text</Text>
<FieldContainer
hasError={!!tokenError}
errorMessage={tokenError}
removeMargin
>
<TextInput
value={token}
scale
@ -98,7 +142,9 @@ const GenerateDeveloperTokenDialog = ({
size={InputSize.base}
onChange={onChange}
maxLength={10000}
hasError={!!tokenError}
/>
</FieldContainer>
</StyledContainer>
</ModalDialog.Body>
<ModalDialog.Footer>
@ -107,7 +153,7 @@ const GenerateDeveloperTokenDialog = ({
primary
scale
onClick={onRevoke}
isDisabled={!token}
isDisabled={!token || !isValidToken}
isLoading={requestRunning}
size={ButtonSize.small}
/>

View File

@ -12,6 +12,7 @@ import {
TConsentData,
TConsentList,
TGenerateDeveloperToken,
TIntrospectDeveloperToken,
} from "../../utils/oauth/types";
export const getClient = async (clientId: string): Promise<IClientProps> => {
@ -273,3 +274,14 @@ export const revokeDeveloperToken = (
true,
);
};
export const introspectDeveloperToken = (token: string) => {
const params = new URLSearchParams();
params.append("token", token);
return request<TIntrospectDeveloperToken>(
{ method: "post", url: "/oauth2/introspect", data: params },
false,
true,
);
};

View File

@ -157,3 +157,19 @@ export type TGenerateDeveloperToken = {
scope: string;
token_type: string;
};
export type TIntrospectDeveloperToken = {
active: boolean;
sub: string;
aud: string[];
nbf: string;
scope: string;
iss: string;
exp: number;
iat: number;
jti: string;
tid: number;
cid: string;
client_id: string;
token_type: string;
};