Client:OAuth2: generate PKCE pair at client

This commit is contained in:
Timofey Boyko 2023-11-22 14:28:29 +03:00
parent 008a2b07ad
commit 312140a0dc
4 changed files with 62 additions and 1 deletions

View File

@ -43,6 +43,7 @@
},
"dependencies": {
"copy-to-clipboard": "^3.3.3",
"crypto-js": "^4.2.0",
"element-resize-detector": "^1.2.4",
"file-saver": "^2.0.5",
"firebase": "^8.10.1",
@ -66,6 +67,7 @@
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.0",
"@svgr/webpack": "^5.5.0",
"@types/crypto-js": "^4.2.1",
"babel-loader": "^8.3.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^9.1.0",

View File

@ -20,6 +20,11 @@ import OnlyofficeDark from "PUBLIC_DIR/images/onlyoffice.dark.react.svg";
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
import Button from "@docspace/components/button";
import { Base } from "@docspace/components/themes";
import {
generateCodeChallenge,
generatePKCEPair,
generateRandomString,
} from "@docspace/common/utils/oauth";
const StyledContainer = styled.div`
width: 100%;
@ -163,6 +168,9 @@ const PreviewDialog = ({
}: PreviewDialogProps) => {
const { t } = useTranslation(["OAuth", "Common"]);
const [codeVerifier, setCodeVerifier] = React.useState("");
const [codeChallenge, setCodeChallenge] = React.useState("");
const onClose = () => setPreviewDialogVisible?.(false);
const icon = theme.isBase ? OnlyofficeLight : OnlyofficeDark;
@ -171,8 +179,19 @@ const PreviewDialog = ({
const encodingScopes = encodeURI(scopesString || "");
const getData = React.useCallback(() => {
const { verifier, challenge } = generatePKCEPair();
setCodeVerifier(verifier);
setCodeChallenge(challenge);
}, []);
React.useEffect(() => {
getData();
}, []);
const getLink = () => {
return `${window.location.origin}/oauth2/authorize?response_type=code&client_id=${client?.clientId}&redirect_uri=${client?.redirectUris[0]}&scope=${encodingScopes}&code_challenge_method=S256&code_challenge=ZX8YUY6qL0EweJXhjDug0S2XuKI8beOWb1LGujmgfuQ`;
return `${window.location.origin}/oauth2/authorize?response_type=code&client_id=${client?.clientId}&redirect_uri=${client?.redirectUris[0]}&scope=${encodingScopes}&code_challenge_method=S256&code_challenge=${codeChallenge}`;
};
const link = getLink();
@ -267,6 +286,24 @@ const PreviewDialog = ({
/>
</div>
</StyledBlocksContainer>
<div className="block-container">
{/* @ts-ignore */}
<Text
fontWeight={600}
lineHeight={"20px"}
fontSize={"13px"}
noSelect
>
Code verifier
</Text>
<Textarea
heightTextArea={64}
enableCopy
isReadOnly
isDisabled
value={codeVerifier}
/>
</div>
</StyledContainer>
</ModalDialog.Body>
<ModalDialog.Footer>

View File

@ -1,3 +1,6 @@
import crypto from "crypto-js";
import sha256 from "crypto-js/sha256";
import { ScopeGroup, ScopeType } from "./enums";
import {
IClientResDTO,
@ -153,3 +156,13 @@ export const filterScopeByGroup = (
return filteredScopes;
};
export function generatePKCEPair() {
const NUM_OF_BYTES = 36; // Total of 44 characters (1 Bytes = 2 char) (standard states that: 43 chars <= verifier <= 128 chars)
const HASH_ALG = "sha256";
const randomVerifier = crypto.lib.WordArray.random(NUM_OF_BYTES).toString();
const hash = sha256(randomVerifier).toString(crypto.enc.Base64);
return { verifier: randomVerifier, challenge: hash };
}

View File

@ -3136,10 +3136,12 @@ __metadata:
"@babel/preset-react": ^7.18.6
"@babel/preset-typescript": ^7.21.0
"@svgr/webpack": ^5.5.0
"@types/crypto-js": ^4.2.1
babel-loader: ^8.3.0
clean-webpack-plugin: ^4.0.0
copy-to-clipboard: ^3.3.3
copy-webpack-plugin: ^9.1.0
crypto-js: ^4.2.0
css-loader: ^6.7.3
element-resize-detector: ^1.2.4
external-remotes-plugin: ^1.0.0
@ -8238,6 +8240,13 @@ __metadata:
languageName: node
linkType: hard
"@types/crypto-js@npm:^4.2.1":
version: 4.2.1
resolution: "@types/crypto-js@npm:4.2.1"
checksum: 2111fb39a879e6c346a657cd4994534b8ff54d34dbfe45f82587e84165af0d9a3b8448c0d9a8ddabc81b5875295ea049462e7b969dbeb7cf24ae205e66dcf15a
languageName: node
linkType: hard
"@types/debug@npm:^4.0.0":
version: 4.1.12
resolution: "@types/debug@npm:4.1.12"